Skip to content

Instantly share code, notes, and snippets.

@pstysz
Created January 17, 2016 14:42
Show Gist options
  • Save pstysz/2e8306e331aef1c03f49 to your computer and use it in GitHub Desktop.
Save pstysz/2e8306e331aef1c03f49 to your computer and use it in GitHub Desktop.
AngularJS_Tutorial
This gist exceeds the recommended number of files (~10). To access all files, please clone this gist.
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="DemoApp node_modules" level="project" />
</component>
</module>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{DemoApp node_modules}" />
</component>
</project>
<component name="libraryTable">
<library name="DemoApp node_modules" type="javaScript">
<properties>
<option name="frameworkName" value="node_modules" />
<sourceFilesUrls>
<item url="file://$PROJECT_DIR$/node_modules" />
</sourceFilesUrls>
</properties>
<CLASSES>
<root url="file://$PROJECT_DIR$/node_modules" />
</CLASSES>
<SOURCES />
</library>
</component>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/DemoApp.iml" filepath="$PROJECT_DIR$/.idea/DemoApp.iml" />
</modules>
</component>
</project>
/* app css stylesheet */
.hoverwell:hover {
background-color: #52575c;
cursor: pointer;
}
select, textarea {
color: #52575c;
}
.well-title {
margin-top: 0px;
}
.session {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.votingWidget {
height: 64px;
padding: 7px;
}
.votingButton {
padding-left:6px;
cursor:pointer;
}
.sessionThumbnail {
height:100%;
overflow: hidden;
}
.calendar{
border: 1px solid #555;
width: 238px;
}
.calendar .row {
margin-left: 0px;
}
.calendar .row div{
float:left;
padding:2px;
width:30px;
text-align:center;
}
.calendar-nav-left {
float:left;
width: 20px;
padding:2px;
}
.calendar-nav-right {
float:left;
width: 20px;
padding:2px;
text-align:right;
}
.calendar .row .calendar-hdr {
padding:2px;
float:left;
width: 166px;
text-align:center;
}
.pointer {
cursor: pointer;
}
.date-picker {
position: absolute;
top: -7px;
left: 209px;
background-color: #272b30;
}
/*!
* Bootstrap v2.3.1
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/.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:31px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;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"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;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:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:21px;color:#c8c8c8;background-color:#272b30}a{color:#fff;text-decoration:none}a:hover,a:focus{color:#fff;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:-20px;*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:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.span12{width:940px}.span11{width:860px}.span10{width:780px}.span9{width:700px}.span8{width:620px}.span7{width:540px}.span6{width:460px}.span5{width:380px}.span4{width:300px}.span3{width:220px}.span2{width:140px}.span1{width:60px}.offset12{margin-left:980px}.offset11{margin-left:900px}.offset10{margin-left:820px}.offset9{margin-left:740px}.offset8{margin-left:660px}.offset7{margin-left:580px}.offset6{margin-left:500px}.offset5{margin-left:420px}.offset4{margin-left:340px}.offset3{margin-left:260px}.offset2{margin-left:180px}.offset1{margin-left:100px}.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:31px;margin-left:2.127659574468085%;*margin-left:2.074468085106383%;-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:2.127659574468085%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.48936170212765%;*width:91.43617021276594%}.row-fluid .span10{width:82.97872340425532%;*width:82.92553191489361%}.row-fluid .span9{width:74.46808510638297%;*width:74.41489361702126%}.row-fluid .span8{width:65.95744680851064%;*width:65.90425531914893%}.row-fluid .span7{width:57.44680851063829%;*width:57.39361702127659%}.row-fluid .span6{width:48.93617021276595%;*width:48.88297872340425%}.row-fluid .span5{width:40.42553191489362%;*width:40.37234042553192%}.row-fluid .span4{width:31.914893617021278%;*width:31.861702127659576%}.row-fluid .span3{width:23.404255319148934%;*width:23.351063829787233%}.row-fluid .span2{width:14.893617021276595%;*width:14.840425531914894%}.row-fluid .span1{width:6.382978723404255%;*width:6.329787234042553%}.row-fluid .offset12{margin-left:104.25531914893617%;*margin-left:104.14893617021275%}.row-fluid .offset12:first-child{margin-left:102.12765957446808%;*margin-left:102.02127659574467%}.row-fluid .offset11{margin-left:95.74468085106382%;*margin-left:95.6382978723404%}.row-fluid .offset11:first-child{margin-left:93.61702127659574%;*margin-left:93.51063829787232%}.row-fluid .offset10{margin-left:87.23404255319149%;*margin-left:87.12765957446807%}.row-fluid .offset10:first-child{margin-left:85.1063829787234%;*margin-left:84.99999999999999%}.row-fluid .offset9{margin-left:78.72340425531914%;*margin-left:78.61702127659572%}.row-fluid .offset9:first-child{margin-left:76.59574468085106%;*margin-left:76.48936170212764%}.row-fluid .offset8{margin-left:70.2127659574468%;*margin-left:70.10638297872339%}.row-fluid .offset8:first-child{margin-left:68.08510638297872%;*margin-left:67.9787234042553%}.row-fluid .offset7{margin-left:61.70212765957446%;*margin-left:61.59574468085106%}.row-fluid .offset7:first-child{margin-left:59.574468085106375%;*margin-left:59.46808510638297%}.row-fluid .offset6{margin-left:53.191489361702125%;*margin-left:53.085106382978715%}.row-fluid .offset6:first-child{margin-left:51.063829787234035%;*margin-left:50.95744680851063%}.row-fluid .offset5{margin-left:44.68085106382979%;*margin-left:44.57446808510638%}.row-fluid .offset5:first-child{margin-left:42.5531914893617%;*margin-left:42.4468085106383%}.row-fluid .offset4{margin-left:36.170212765957444%;*margin-left:36.06382978723405%}.row-fluid .offset4:first-child{margin-left:34.04255319148936%;*margin-left:33.93617021276596%}.row-fluid .offset3{margin-left:27.659574468085104%;*margin-left:27.5531914893617%}.row-fluid .offset3:first-child{margin-left:25.53191489361702%;*margin-left:25.425531914893618%}.row-fluid .offset2{margin-left:19.148936170212764%;*margin-left:19.04255319148936%}.row-fluid .offset2:first-child{margin-left:17.02127659574468%;*margin-left:16.914893617021278%}.row-fluid .offset1{margin-left:10.638297872340425%;*margin-left:10.53191489361702%}.row-fluid .offset1:first-child{margin-left:8.51063829787234%;*margin-left:8.404255319148938%}[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:20px;padding-left:20px;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:21px;font-weight:200;line-height:31.5px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#7a8288}a.muted:hover,a.muted:focus{color:#62686d}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-error{color:#b94a48}a.text-error:hover,a.text-error:focus{color:#953b39}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10.5px 0;font-family:inherit;font-weight:bold;line-height:21px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#7a8288}h1,h2,h3{line-height:42px}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:9.5px;margin:21px 0 31.5px;border-bottom:1px solid #bbbfc2}ul,ol{padding:0;margin:0 0 10.5px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:21px}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:21px}dt,dd{line-height:21px}dt{font-weight:bold}dd{margin-left:10.5px}.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:21px 0;border:0;border-top:1px solid #1c1e22;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #7a8288}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 21px;border-left:5px solid #bbbfc2}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:21px;color:#7a8288}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #bbbfc2;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:21px;font-style:normal;line-height:21px}code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,Consolas,"Courier New",monospace;font-size:12px;color:#3a3f44;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre{display:block;padding:10px;margin:0 0 10.5px;font-size:13px;line-height:21px;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);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}pre.prettyprint{margin-bottom:21px}pre 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}form{margin:0 0 21px}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:21px;line-height:42px;color:#3a3f44;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15.75px;color:#7a8288}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:21px}input,button,select,textarea{font-family:"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:21px;padding:4px 6px;margin-bottom:10.5px;font-size:14px;line-height:21px;color:#52575c;vertical-align:middle;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}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-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border 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:rgba(82,168,236,0.8);outline:0;outline:thin dotted \9;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(82,168,236,0.6)}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:31px;*margin-top:4px;line-height:31px}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:#7a8288;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:#7a8288}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#7a8288}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#7a8288}.radio,.checkbox{min-height:21px;padding-left:20px}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-20px}.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}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:926px}input.span11,textarea.span11,.uneditable-input.span11{width:846px}input.span10,textarea.span10,.uneditable-input.span10{width:766px}input.span9,textarea.span9,.uneditable-input.span9{width:686px}input.span8,textarea.span8,.uneditable-input.span8{width:606px}input.span7,textarea.span7,.uneditable-input.span7{width:526px}input.span6,textarea.span6,.uneditable-input.span6{width:446px}input.span5,textarea.span5,.uneditable-input.span5{width:366px}input.span4,textarea.span4,.uneditable-input.span4{width:286px}input.span3,textarea.span3,.uneditable-input.span3{width:206px}input.span2,textarea.span2,.uneditable-input.span2{width:126px}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:#bbbfc2}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.control-group.warning .control-label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853}.control-group.warning .checkbox,.control-group.warning .radio,.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853}.control-group.warning input,.control-group.warning select,.control-group.warning textarea{border-color:#c09853;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.control-group.error .control-label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48}.control-group.error .checkbox,.control-group.error .radio,.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48}.control-group.error input,.control-group.error select,.control-group.error textarea{border-color:#b94a48;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.control-group.success .control-label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847}.control-group.success .checkbox,.control-group.success .radio,.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847}.control-group.success input,.control-group.success select,.control-group.success textarea{border-color:#468847;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847}.control-group.info .control-label,.control-group.info .help-block,.control-group.info .help-inline{color:#3a87ad}.control-group.info .checkbox,.control-group.info .radio,.control-group.info input,.control-group.info select,.control-group.info textarea{color:#3a87ad}.control-group.info input,.control-group.info select,.control-group.info textarea{border-color:#3a87ad;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.control-group.info input:focus,.control-group.info select:focus,.control-group.info textarea:focus{border-color:#2d6987;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7ab5d3}.control-group.info .input-prepend .add-on,.control-group.info .input-append .add-on{color:#3a87ad;background-color:#d9edf7;border-color:#3a87ad}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#b94a48;border-color:#ee5f5b}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7}.form-actions{padding:20px 20px 21px;margin-top:21px;margin-bottom:21px;background-color:#202328;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:#eee}.help-block{display:block;margin-bottom:10.5px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10.5px;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;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.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:21px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:21px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#bbbfc2;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:#d1eed1;border-color:#62c462}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-prepend .add-on:first-child,.input-prepend .btn:first-child{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input,.input-append select,.input-append .uneditable-input{-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-append input+.btn-group .btn:last-child,.input-append select+.btn-group .btn:last-child,.input-append .uneditable-input+.btn-group .btn:last-child{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-append .add-on:last-child,.input-append .btn:last-child,.input-append .btn-group:last-child>.dropdown-toggle{-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.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 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:14px;padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin-bottom:0;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.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:middle;*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:10.5px}legend+.control-group{margin-top:21px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:21px;*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:10.5px}.form-horizontal .form-actions{padding-left:180px}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:21px}.table th,.table td{padding:8px;line-height:21px;text-align:left;vertical-align:top;border-top:1px solid #1c1e22}.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 #1c1e22}.table .table{background-color:#272b30}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #1c1e22;border-collapse:separate;*border-collapse:collapse;border-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.table-bordered th,.table-bordered td{border-left:1px solid #1c1e22}.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{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.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{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.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{-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px}.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{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{-webkit-border-bottom-left-radius:0;border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{-webkit-border-bottom-right-radius:0;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{-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topleft:4px}.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{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-topright:4px}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#2e3236}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#3a3f44}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:124px;margin-left:0}.table td.span3,.table th.span3{float:none;width:204px;margin-left:0}.table td.span4,.table th.span4{float:none;width:284px;margin-left:0}.table td.span5,.table th.span5{float:none;width:364px;margin-left:0}.table td.span6,.table th.span6{float:none;width:444px;margin-left:0}.table td.span7,.table th.span7{float:none;width:524px;margin-left:0}.table td.span8,.table th.span8{float:none;width:604px;margin-left:0}.table td.span9,.table th.span9{float:none;width:684px;margin-left:0}.table td.span10,.table th.span10{float:none;width:764px;margin-left:0}.table td.span11,.table th.span11{float:none;width:844px;margin-left:0}.table td.span12,.table th.span12{float:none;width:924px;margin-left:0}.table tbody tr.success>td{background-color:#dff0d8}.table tbody tr.error>td{background-color:#f2dede}.table tbody tr.warning>td{background-color:#fcf8e3}.table tbody tr.info>td{background-color:#d9edf7}.table-hover tbody tr.success:hover>td{background-color:#d0e9c6}.table-hover tbody tr.error:hover>td{background-color:#ebcccc}.table-hover tbody tr.warning:hover>td{background-color:#faf2cc}.table-hover tbody tr.info:hover>td{background-color:#c4e3f3}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;margin-top:1px;*margin-right:.3em;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat}.icon-white,.nav-pills>.active>a>[class^="icon-"],.nav-pills>.active>a>[class*=" icon-"],.nav-list>.active>a>[class^="icon-"],.nav-list>.active>a>[class*=" icon-"],.navbar-inverse .nav>.active>a>[class^="icon-"],.navbar-inverse .nav>.active>a>[class*=" icon-"],.dropdown-menu>li>a:hover>[class^="icon-"],.dropdown-menu>li>a:focus>[class^="icon-"],.dropdown-menu>li>a:hover>[class*=" icon-"],.dropdown-menu>li>a:focus>[class*=" icon-"],.dropdown-menu>.active>a>[class^="icon-"],.dropdown-menu>.active>a>[class*=" icon-"],.dropdown-submenu:hover>a>[class^="icon-"],.dropdown-submenu:focus>a>[class^="icon-"],.dropdown-submenu:hover>a>[class*=" icon-"],.dropdown-submenu:focus>a>[class*=" icon-"]{background-image:url("../img/glyphicons-halflings-white.png")}.icon-glass{background-position:0 0}.icon-music{background-position:-24px 0}.icon-search{background-position:-48px 0}.icon-envelope{background-position:-72px 0}.icon-heart{background-position:-96px 0}.icon-star{background-position:-120px 0}.icon-star-empty{background-position:-144px 0}.icon-user{background-position:-168px 0}.icon-film{background-position:-192px 0}.icon-th-large{background-position:-216px 0}.icon-th{background-position:-240px 0}.icon-th-list{background-position:-264px 0}.icon-ok{background-position:-288px 0}.icon-remove{background-position:-312px 0}.icon-zoom-in{background-position:-336px 0}.icon-zoom-out{background-position:-360px 0}.icon-off{background-position:-384px 0}.icon-signal{background-position:-408px 0}.icon-cog{background-position:-432px 0}.icon-trash{background-position:-456px 0}.icon-home{background-position:0 -24px}.icon-file{background-position:-24px -24px}.icon-time{background-position:-48px -24px}.icon-road{background-position:-72px -24px}.icon-download-alt{background-position:-96px -24px}.icon-download{background-position:-120px -24px}.icon-upload{background-position:-144px -24px}.icon-inbox{background-position:-168px -24px}.icon-play-circle{background-position:-192px -24px}.icon-repeat{background-position:-216px -24px}.icon-refresh{background-position:-240px -24px}.icon-list-alt{background-position:-264px -24px}.icon-lock{background-position:-287px -24px}.icon-flag{background-position:-312px -24px}.icon-headphones{background-position:-336px -24px}.icon-volume-off{background-position:-360px -24px}.icon-volume-down{background-position:-384px -24px}.icon-volume-up{background-position:-408px -24px}.icon-qrcode{background-position:-432px -24px}.icon-barcode{background-position:-456px -24px}.icon-tag{background-position:0 -48px}.icon-tags{background-position:-25px -48px}.icon-book{background-position:-48px -48px}.icon-bookmark{background-position:-72px -48px}.icon-print{background-position:-96px -48px}.icon-camera{background-position:-120px -48px}.icon-font{background-position:-144px -48px}.icon-bold{background-position:-167px -48px}.icon-italic{background-position:-192px -48px}.icon-text-height{background-position:-216px -48px}.icon-text-width{background-position:-240px -48px}.icon-align-left{background-position:-264px -48px}.icon-align-center{background-position:-288px -48px}.icon-align-right{background-position:-312px -48px}.icon-align-justify{background-position:-336px -48px}.icon-list{background-position:-360px -48px}.icon-indent-left{background-position:-384px -48px}.icon-indent-right{background-position:-408px -48px}.icon-facetime-video{background-position:-432px -48px}.icon-picture{background-position:-456px -48px}.icon-pencil{background-position:0 -72px}.icon-map-marker{background-position:-24px -72px}.icon-adjust{background-position:-48px -72px}.icon-tint{background-position:-72px -72px}.icon-edit{background-position:-96px -72px}.icon-share{background-position:-120px -72px}.icon-check{background-position:-144px -72px}.icon-move{background-position:-168px -72px}.icon-step-backward{background-position:-192px -72px}.icon-fast-backward{background-position:-216px -72px}.icon-backward{background-position:-240px -72px}.icon-play{background-position:-264px -72px}.icon-pause{background-position:-288px -72px}.icon-stop{background-position:-312px -72px}.icon-forward{background-position:-336px -72px}.icon-fast-forward{background-position:-360px -72px}.icon-step-forward{background-position:-384px -72px}.icon-eject{background-position:-408px -72px}.icon-chevron-left{background-position:-432px -72px}.icon-chevron-right{background-position:-456px -72px}.icon-plus-sign{background-position:0 -96px}.icon-minus-sign{background-position:-24px -96px}.icon-remove-sign{background-position:-48px -96px}.icon-ok-sign{background-position:-72px -96px}.icon-question-sign{background-position:-96px -96px}.icon-info-sign{background-position:-120px -96px}.icon-screenshot{background-position:-144px -96px}.icon-remove-circle{background-position:-168px -96px}.icon-ok-circle{background-position:-192px -96px}.icon-ban-circle{background-position:-216px -96px}.icon-arrow-left{background-position:-240px -96px}.icon-arrow-right{background-position:-264px -96px}.icon-arrow-up{background-position:-289px -96px}.icon-arrow-down{background-position:-312px -96px}.icon-share-alt{background-position:-336px -96px}.icon-resize-full{background-position:-360px -96px}.icon-resize-small{background-position:-384px -96px}.icon-plus{background-position:-408px -96px}.icon-minus{background-position:-433px -96px}.icon-asterisk{background-position:-456px -96px}.icon-exclamation-sign{background-position:0 -120px}.icon-gift{background-position:-24px -120px}.icon-leaf{background-position:-48px -120px}.icon-fire{background-position:-72px -120px}.icon-eye-open{background-position:-96px -120px}.icon-eye-close{background-position:-120px -120px}.icon-warning-sign{background-position:-144px -120px}.icon-plane{background-position:-168px -120px}.icon-calendar{background-position:-192px -120px}.icon-random{width:16px;background-position:-216px -120px}.icon-comment{background-position:-240px -120px}.icon-magnet{background-position:-264px -120px}.icon-chevron-up{background-position:-288px -120px}.icon-chevron-down{background-position:-313px -119px}.icon-retweet{background-position:-336px -120px}.icon-shopping-cart{background-position:-360px -120px}.icon-folder-close{width:16px;background-position:-384px -120px}.icon-folder-open{width:16px;background-position:-408px -120px}.icon-resize-vertical{background-position:-432px -119px}.icon-resize-horizontal{background-position:-456px -118px}.icon-hdd{background-position:0 -144px}.icon-bullhorn{background-position:-24px -144px}.icon-bell{background-position:-48px -144px}.icon-certificate{background-position:-72px -144px}.icon-thumbs-up{background-position:-96px -144px}.icon-thumbs-down{background-position:-120px -144px}.icon-hand-right{background-position:-144px -144px}.icon-hand-left{background-position:-168px -144px}.icon-hand-up{background-position:-192px -144px}.icon-hand-down{background-position:-216px -144px}.icon-circle-arrow-right{background-position:-240px -144px}.icon-circle-arrow-left{background-position:-264px -144px}.icon-circle-arrow-up{background-position:-288px -144px}.icon-circle-arrow-down{background-position:-312px -144px}.icon-globe{background-position:-336px -144px}.icon-wrench{background-position:-360px -144px}.icon-tasks{background-position:-384px -144px}.icon-filter{background-position:-408px -144px}.icon-briefcase{background-position:-432px -144px}.icon-fullscreen{background-position:-456px -144px}.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:#3a3f44;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9.5px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:transparent;border-bottom:1px solid #1c1e22}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:21px;color:#7a8288;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:#22262a;background-image:-moz-linear-gradient(top,#272b30,#1c1e22);background-image:-webkit-gradient(linear,0 0,0 100%,from(#272b30),to(#1c1e22));background-image:-webkit-linear-gradient(top,#272b30,#1c1e22);background-image:-o-linear-gradient(top,#272b30,#1c1e22);background-image:linear-gradient(to bottom,#272b30,#1c1e22);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff272b30',endColorstr='#ff1c1e22',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#22262a;background-image:-moz-linear-gradient(top,#272b30,#1c1e22);background-image:-webkit-gradient(linear,0 0,0 100%,from(#272b30),to(#1c1e22));background-image:-webkit-linear-gradient(top,#272b30,#1c1e22);background-image:-o-linear-gradient(top,#272b30,#1c1e22);background-image:linear-gradient(to bottom,#272b30,#1c1e22);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff272b30',endColorstr='#ff1c1e22',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#7a8288}.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;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;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;-webkit-border-radius:5px 5px 5px 0;-moz-border-radius:5px 5px 5px 0;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:#0b0c0d;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;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#202328;border:1px solid #101214;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-moz-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;-moz-transition:height .35s ease;-o-transition:height .35s ease;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:21px;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{display:inline-block;*display:inline;padding:4px 12px;margin-bottom:0;*margin-left:.3em;font-size:14px;line-height:21px;color:#3a3f44;text-align:center;text-shadow:0 1px 1px rgba(255,255,255,0.75);vertical-align:middle;cursor:pointer;background-color:#f5f5f5;*background-color:#e6e6e6;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border:1px solid #ccc;*border:0;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);*zoom:1;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#3a3f44;background-color:#e6e6e6;*background-color:#d9d9d9}.btn:active,.btn.active{background-color:#ccc \9}.btn:first-child{*margin-left:0}.btn:hover,.btn:focus{color:#3a3f44;text-decoration:none;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]{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-large{padding:11px 19px;font-size:17.5px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.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:3px;-moz-border-radius:3px;border-radius:3px}.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-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.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:#bbbec2;*background-color:#bbbdc2;background-image:-moz-linear-gradient(top,#bbbfc2,#bbbdc2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#bbbfc2),to(#bbbdc2));background-image:-webkit-linear-gradient(top,#bbbfc2,#bbbdc2);background-image:-o-linear-gradient(top,#bbbfc2,#bbbdc2);background-image:linear-gradient(to bottom,#bbbfc2,#bbbdc2);background-repeat:repeat-x;border-color:#bbbdc2 #bbbdc2 #93969e;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='#ffbbbfc2',endColorstr='#ffbbbdc2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#bbbdc2;*background-color:#aeb0b6}.btn-primary:active,.btn-primary.active{background-color:#a0a3aa \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;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='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;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='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;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='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \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,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*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:#41454a;*background-color:#272b30;background-image:-moz-linear-gradient(top,#52575c,#272b30);background-image:-webkit-gradient(linear,0 0,0 100%,from(#52575c),to(#272b30));background-image:-webkit-linear-gradient(top,#52575c,#272b30);background-image:-o-linear-gradient(top,#52575c,#272b30);background-image:linear-gradient(to bottom,#52575c,#272b30);background-repeat:repeat-x;border-color:#272b30 #272b30 #050506;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='#ff52575c',endColorstr='#ff272b30',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#272b30;*background-color:#1c1e22}.btn-inverse:active,.btn-inverse.active{background-color:#101214 \9}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:#fff;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#fff;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#3a3f44;text-decoration: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:10.5px;margin-bottom:10.5px;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;-webkit-border-radius:0;-moz-border-radius:0;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;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.btn-group>.btn.large:first-child{margin-left:0;-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.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;-webkit-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);-moz-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);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;-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-group.open .btn.dropdown-toggle{background-color:#e6e6e6}.btn-group.open .btn-primary.dropdown-toggle{background-color:#bbbdc2}.btn-group.open .btn-warning.dropdown-toggle{background-color:#f89406}.btn-group.open .btn-danger.dropdown-toggle{background-color:#bd362f}.btn-group.open .btn-success.dropdown-toggle{background-color:#51a351}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#272b30}.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%;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.btn-group-vertical>.btn:last-child{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px}.btn-group-vertical>.btn-large:first-child{-webkit-border-radius:6px 6px 0 0;-moz-border-radius:6px 6px 0 0;border-radius:6px 6px 0 0}.btn-group-vertical>.btn-large:last-child{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.alert{padding:8px 35px 8px 14px;margin-bottom:21px;text-shadow:0 1px 0 rgba(255,255,255,0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.alert,.alert h4{color:#c09853}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:21px}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success h4{color:#468847}.alert-danger,.alert-error{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger h4,.alert-error h4{color:#b94a48}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info h4{color:#3a87ad}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.nav{margin-bottom:21px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#bbbfc2}.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:21px;color:#7a8288;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:#fff}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9.5px 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:12px;padding-left:12px;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:8px;padding-bottom:8px;line-height:21px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#bbbfc2 #bbbfc2 #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#52575c;cursor:default;background-color:#272b30;border:1px solid #ddd;border-bottom-color:transparent}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#fff}.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;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-top-left-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{-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-webkit-border-bottom-left-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}.nav-tabs .dropdown-menu{-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#fff;border-bottom-color:#fff}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.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:#52575c;border-bottom-color:#52575c}.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{color:#fff;background-color:#7a8288;border-color:#7a8288}.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:#7a8288}.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{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;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;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#bbbfc2 #ddd #bbbfc2 #bbbfc2}.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;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#bbbfc2 #bbbfc2 #bbbfc2 #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:#7a8288}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:21px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#32373c;background-image:-moz-linear-gradient(top,#3a3f44,#272b30);background-image:-webkit-gradient(linear,0 0,0 100%,from(#3a3f44),to(#272b30));background-image:-webkit-linear-gradient(top,#3a3f44,#272b30);background-image:-o-linear-gradient(top,#3a3f44,#272b30);background-image:linear-gradient(to bottom,#3a3f44,#272b30);background-repeat:repeat-x;border:1px solid #0c0d0e;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3a3f44',endColorstr='#ff272b30',GradientType=0);*zoom:1;-webkit-box-shadow:0 1px 4px rgba(0,0,0,0.065);-moz-box-shadow:0 1px 4px rgba(0,0,0,0.065);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:9.5px 20px 9.5px;margin-left:-20px;font-size:20px;font-weight:200;color:#bbbfc2;text-shadow:0 1px 0 #3a3f44}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#c8c8c8}.navbar-link{color:#c8c8c8}.navbar-link:hover,.navbar-link:focus{color:#fff}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #3a3f44;border-left:1px solid #272b30}.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:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{-webkit-border-radius:0;-moz-border-radius:0;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;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{-webkit-box-shadow:0 1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,0.1);box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{-webkit-box-shadow:0 -1px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 -1px 10px rgba(0,0,0,0.1);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:9.5px 15px 9.5px;color:#c8c8c8;text-decoration:none;text-shadow:0 1px 0 #3a3f44}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#fff;text-decoration:none;background-color:#3a3f44}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#fff;text-decoration:none;background-color:#272b30;-webkit-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);-moz-box-shadow:inset 0 3px 8px rgba(0,0,0,0.125);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:#272a2e;*background-color:#1c1e22;background-image:-moz-linear-gradient(top,#2e3236,#1c1e22);background-image:-webkit-gradient(linear,0 0,0 100%,from(#2e3236),to(#1c1e22));background-image:-webkit-linear-gradient(top,#2e3236,#1c1e22);background-image:-o-linear-gradient(top,#2e3236,#1c1e22);background-image:linear-gradient(to bottom,#2e3236,#1c1e22);background-repeat:repeat-x;border-color:#1c1e22 #1c1e22 #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='#ff2e3236',endColorstr='#ff1c1e22',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075);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,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;background-color:#1c1e22;*background-color:#101214}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#050506 \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,0.25);-moz-box-shadow:0 1px 0 rgba(0,0,0,0.25);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 #3a3f44;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 #3a3f44;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#272b30}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#c8c8c8;border-bottom-color:#c8c8c8}.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:#fff;border-bottom-color:#fff}.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;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1e2125;background-image:-moz-linear-gradient(top,#272b30,#101214);background-image:-webkit-gradient(linear,0 0,0 100%,from(#272b30),to(#101214));background-image:-webkit-linear-gradient(top,#272b30,#101214);background-image:-o-linear-gradient(top,#272b30,#101214);background-image:linear-gradient(to bottom,#272b30,#101214);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff272b30',endColorstr='#ff101214',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#7a8288;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:#7a8288}.navbar-inverse .navbar-text{color:#7a8288}.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:#101214}.navbar-inverse .navbar-link{color:#7a8288}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#272b30;border-left-color:#101214}.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:#101214}.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:#7a8288;border-bottom-color:#7a8288}.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:#49515a;border-color:#101214;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);-webkit-transition:none;-moz-transition:none;-o-transition:none;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:#3a3f44;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;-webkit-box-shadow:0 0 3px rgba(0,0,0,0.15);-moz-box-shadow:0 0 3px rgba(0,0,0,0.15);box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#121417;*background-color:#050506;background-image:-moz-linear-gradient(top,#1c1e22,#050506);background-image:-webkit-gradient(linear,0 0,0 100%,from(#1c1e22),to(#050506));background-image:-webkit-linear-gradient(top,#1c1e22,#050506);background-image:-o-linear-gradient(top,#1c1e22,#050506);background-image:linear-gradient(to bottom,#1c1e22,#050506);background-repeat:repeat-x;border-color:#050506 #050506 #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='#ff1c1e22',endColorstr='#ff050506',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.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:#050506;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 21px;list-style:none;background-color:#f5f5f5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.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:#7a8288}.pagination{margin:21px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;*zoom:1;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:21px;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:#7a8288;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#7a8288;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-moz-border-radius-topleft:4px}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-bottomright:4px}.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{-webkit-border-bottom-left-radius:6px;border-bottom-left-radius:6px;-webkit-border-top-left-radius:6px;border-top-left-radius:6px;-moz-border-radius-bottomleft:6px;-moz-border-radius-topleft:6px}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{-webkit-border-top-right-radius:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;border-bottom-right-radius:6px;-moz-border-radius-topright:6px;-moz-border-radius-bottomright:6px}.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{-webkit-border-bottom-left-radius:3px;border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-top-left-radius:3px;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px}.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{-webkit-border-top-right-radius:3px;border-top-right-radius:3px;-webkit-border-bottom-right-radius:3px;border-bottom-right-radius:3px;-moz-border-radius-topright:3px;-moz-border-radius-bottomright:3px}.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:21px 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;-webkit-border-radius:15px;-moz-border-radius:15px;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:#7a8288;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;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;outline:0;-webkit-box-shadow:0 3px 7px rgba(0,0,0,0.3);-moz-box-shadow:0 3px 7px rgba(0,0,0,0.3);box-shadow:0 3px 7px rgba(0,0,0,0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.modal.fade{top:-25%;-webkit-transition:opacity .3s linear,top .3s ease-out;-moz-transition:opacity .3s linear,top .3s ease-out;-o-transition:opacity .3s linear,top .3s ease-out;transition:opacity .3s linear,top .3s ease-out}.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;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;*zoom:1;-webkit-box-shadow:inset 0 1px 0 #fff;-moz-box-shadow:inset 0 1px 0 #fff;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:#3a3f44;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#3a3f44;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#3a3f44;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#3a3f44;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#3a3f44;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:#3a3f44;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;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:#41474c;border-bottom:1px solid #353a3e;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;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:#3a3f44;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:#3a3f44;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:#3a3f44;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:#3a3f44;border-right-width:0}.thumbnails{margin-left:-20px;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:21px;margin-left:20px}.thumbnail{display:block;padding:4px;line-height:21px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-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:#fff;-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:#52575c}.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:#7a8288}.label{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.badge{padding-right:9px;padding-left:9px;-webkit-border-radius:9px;-moz-border-radius: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:#b94a48}.label-important[href],.badge-important[href]{background-color:#953b39}.label-warning,.badge-warning{background-color:#f89406}.label-warning[href],.badge-warning[href]{background-color:#c67605}.label-success,.badge-success{background-color:#468847}.label-success[href],.badge-success[href]{background-color:#356635}.label-info,.badge-info{background-color:#3a87ad}.label-info[href],.badge-info[href]{background-color:#2d6987}.label-inverse,.badge-inverse{background-color:#3a3f44}.label-inverse[href],.badge-inverse[href]{background-color:#232628}.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}}@-moz-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:0 0}to{background-position:40px 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:21px;margin-bottom:21px;overflow:hidden;background-color:#f7f7f7;background-image:-moz-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f9f9f9));background-image:-webkit-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:-o-linear-gradient(top,#f5f5f5,#f9f9f9);background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top,#149bdf,#0480be);background-image:-webkit-gradient(linear,0 0,0 100%,from(#149bdf),to(#0480be));background-image:-webkit-linear-gradient(top,#149bdf,#0480be);background-image:-o-linear-gradient(top,#149bdf,#0480be);background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width .6s ease;-moz-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress .bar+.bar{-webkit-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);-moz-box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15);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:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;-ms-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:-moz-linear-gradient(top,#ee5f5b,#c43c35);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#c43c35));background-image:-webkit-linear-gradient(top,#ee5f5b,#c43c35);background-image:-o-linear-gradient(top,#ee5f5b,#c43c35);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:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:-moz-linear-gradient(top,#62c462,#57a957);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#57a957));background-image:-webkit-linear-gradient(top,#62c462,#57a957);background-image:-o-linear-gradient(top,#62c462,#57a957);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:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:-moz-linear-gradient(top,#5bc0de,#339bb9);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#339bb9));background-image:-webkit-linear-gradient(top,#5bc0de,#339bb9);background-image:-o-linear-gradient(top,#5bc0de,#339bb9);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:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#faa732;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450',endColorstr='#fff89406',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#fbb450;background-image:-webkit-gradient(linear,0 100%,100% 0,color-stop(0.25,rgba(255,255,255,0.15)),color-stop(0.25,transparent),color-stop(0.5,transparent),color-stop(0.5,rgba(255,255,255,0.15)),color-stop(0.75,rgba(255,255,255,0.15)),color-stop(0.75,transparent),to(transparent));background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-moz-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:21px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.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:21px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-moz-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;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:#272b30;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;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:#3a3f44;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:21px;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:31.5px;color:inherit;background-color:#202328;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:31.5px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}h1,h2,h3,h4,h5,h6{text-shadow:-1px -1px 0 rgba(0,0,0,0.3)}code,pre{text-shadow:none;background-color:#f7f7f7;border:1px solid #1c1e22}legend,.page-header{border-bottom:1px solid #1c1e22}hr{border-bottom:0}.navbar .navbar-inner{background-color:#3a3f44;background-image:-webkit-gradient(linear,0 0,0 100%,from(#52575c),color-stop(70%,#3a3f44),to(#3a3f44));background-image:-webkit-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:-moz-linear-gradient(top,#52575c,#3a3f44 70%,#3a3f44);background-image:-o-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff52575c',endColorstr='#ff3a3f44',GradientType=0)}.navbar .brand{font-weight:bold;text-shadow:1px 1px 1px rgba(0,0,0,0.3);border-right:1px solid #2e3134}.navbar .navbar-text{padding:0 15px;font-weight:bold}.navbar .nav>li>a{text-shadow:1px 1px 1px rgba(0,0,0,0.3);border-right:1px solid rgba(0,0,0,0.2);border-left:1px solid rgba(255,255,255,0.1)}.navbar .nav>li>a:hover{background-color:#3a3f44;background-image:-moz-linear-gradient(280deg,#272b30,#3a3f44);background-image:-webkit-linear-gradient(280deg,#272b30,#3a3f44);background-image:-o-linear-gradient(280deg,#272b30,#3a3f44);background-image:linear-gradient(280deg,#272b30,#3a3f44);background-repeat:repeat-x;border-right:1px solid transparent;border-left:1px solid transparent}.navbar .nav>li.active>a,.navbar .nav>li.active>a:hover{color:#bbbfc2;background-color:#3a3f44;background-color:#43494f;background-image:-moz-linear-gradient(280deg,#30353b,#43494f);background-image:-webkit-linear-gradient(280deg,#30353b,#43494f);background-image:-o-linear-gradient(280deg,#30353b,#43494f);background-image:linear-gradient(280deg,#30353b,#43494f);background-repeat:repeat-x;border-right:1px solid #2e3134}.navbar .navbar-search .search-query{border:1px solid #2e3134}.navbar .btn,.navbar .btn-group{margin:4px 0}.navbar .divider-vertical{background-color:transparent;border-right:0}.navbar .dropdown-menu::after{border-bottom:6px solid #3a3f44}.navbar-inverse .navbar-inner{background-color:#151719;background-image:-webkit-gradient(linear,0 0,0 100%,from(#202328),color-stop(70%,#151719),to(#151719));background-image:-webkit-linear-gradient(#202328,#151719 70%,#151719);background-image:-moz-linear-gradient(top,#202328,#151719 70%,#151719);background-image:-o-linear-gradient(#202328,#151719 70%,#151719);background-image:linear-gradient(#202328,#151719 70%,#151719);background-repeat:no-repeat;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff202328',endColorstr='#ff151719',GradientType=0)}.navbar-inverse .nav li>a{background-color:transparent;background-image:none}.navbar-inverse .nav li>a:hover,.navbar-inverse .nav li.active>a,.navbar-inverse .nav li.active>a:hover{background-color:#1c1e22;background-image:-moz-linear-gradient(280deg,#101214,#1c1e22);background-image:-webkit-linear-gradient(280deg,#101214,#1c1e22);background-image:-o-linear-gradient(280deg,#101214,#1c1e22);background-image:linear-gradient(280deg,#101214,#1c1e22);background-repeat:repeat-x}@media(max-width:979px){.navbar .nav-collapse .nav li>a,.navbar .nav-collapse .nav li>a:hover,.navbar .nav-collapse .nav .active>a,.navbar .nav-collapse .nav .active>a:hover{color:#bbbfc2;background-color:transparent;background-image:none;border:1px solid transparent;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.navbar .nav-collapse .nav li>a:hover,.navbar .nav-collapse .nav .active>a:hover{background-color:#272b30}.navbar .nav-collapse .navbar-form,.navbar .nav-collapse .navbar-search{border-color:transparent;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.navbar .nav-collapse .nav-header{color:#7a8288}.navbar-inverse .nav-collapse .nav li>a:hover,.navbar-inverse .nav-collapse .nav .active>a:hover{background-color:#272b30!important}}div.subnav{margin:0 1px;background-color:#3a3f44;background-image:-webkit-gradient(linear,0 0,0 100%,from(#52575c),color-stop(70%,#3a3f44),to(#3a3f44));background-image:-webkit-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:-moz-linear-gradient(top,#52575c,#3a3f44 70%,#3a3f44);background-image:-o-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-repeat:no-repeat;border:1px solid transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff52575c',endColorstr='#ff3a3f44',GradientType=0);-webkit-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';-moz-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)'}div.subnav .nav>li>a{color:#bbbfc2;text-shadow:1px 1px 1px rgba(0,0,0,0.3);border-right:1px solid #2e3134;border-left:1px solid #52575c}div.subnav .nav>li>a:hover{color:#bbbfc2;background-color:#3a3f44;background-image:-moz-linear-gradient(280deg,#272b30,#3a3f44);background-image:-webkit-linear-gradient(280deg,#272b30,#3a3f44);background-image:-o-linear-gradient(280deg,#272b30,#3a3f44);background-image:linear-gradient(280deg,#272b30,#3a3f44);background-repeat:repeat-x;border-right:1px solid transparent;border-left:1px solid transparent}div.subnav .nav>li.active>a,div.subnav .nav>li.active>a:hover{color:#bbbfc2;background-color:#3a3f44;background-color:#43494f;background-image:-moz-linear-gradient(280deg,#30353b,#43494f);background-image:-webkit-linear-gradient(280deg,#30353b,#43494f);background-image:-o-linear-gradient(280deg,#30353b,#43494f);background-image:linear-gradient(280deg,#30353b,#43494f);background-repeat:repeat-x;border-right:1px solid #2e3134}div.subnav .nav>li:first-child>a,div.subnav .nav>li:first-child>a:hover{border-left:1px solid transparent}div.subnav .nav>li.active:last-child>a,div.subnav .nav>li:last-child>a:hover{border-right:1px solid #2e3134}div.subnav .open .dropdown-toggle{border-right:1px solid #2e3134;border-left:1px solid #52575c}div.subnav.subnav-fixed{top:40px;margin:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}div.subnav.subnav-fixed .nav>li.active:first-child>a,div.subnav.subnav-fixed .nav>li:first-child>a:hover{border-left:1px solid #2e3134}.nav .nav-header{text-shadow:-1px -1px 0 rgba(0,0,0,0.3)}.nav>li>a{font-weight:bold;color:#7a8288;text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-color:#3a3f44;background-image:-webkit-gradient(linear,0 0,0 100%,from(#52575c),color-stop(70%,#3a3f44),to(#3a3f44));background-image:-webkit-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:-moz-linear-gradient(top,#52575c,#3a3f44 70%,#3a3f44);background-image:-o-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-repeat:no-repeat;border:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff52575c',endColorstr='#ff3a3f44',GradientType=0);-webkit-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';-moz-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)'}.nav li.active>a,.nav li.active>a:hover{color:#fff;background-color:transparent;border:0}.nav li>a:hover{color:#bbbfc2;background-color:transparent}.nav-list{background-color:#3a3f44;-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.4);-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.4);box-shadow:1px 1px 1px rgba(0,0,0,0.4)}.nav-list li>a{text-shadow:-1px -1px 0 rgba(0,0,0,0.3);background-image:none}.nav-list .nav-header{color:#52575c;text-shadow:-1px -1px 0 rgba(0,0,0,0.3)}.nav-list .divider{background-color:transparent;border-bottom:1px solid #1c1e22}.nav-tabs{border-bottom:0}.tabs-below .nav-tabs{border-top:0}.tabs-left .nav-tabs{border-right:0}.tabs-right .nav-tabs{border-left:none}.nav-tabs.nav-stacked li>a,.nav-tabs.nav-stacked li>a:hover{background-image:none;border:1px solid #1c1e22}.nav-tabs.nav-stacked li>a:hover,.nav-tabs.nav-stacked .active>a,.nav-tabs.nav-stacked .active>a:hover{color:#fff;background-color:#202328}.breadcrumb{background-color:#3a3f44;background-image:-webkit-gradient(linear,0 0,0 100%,from(#52575c),color-stop(70%,#3a3f44),to(#3a3f44));background-image:-webkit-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:-moz-linear-gradient(top,#52575c,#3a3f44 70%,#3a3f44);background-image:-o-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-repeat:no-repeat;border:1px solid transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff52575c',endColorstr='#ff3a3f44',GradientType=0);-webkit-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';-moz-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)'}.breadcrumb a{font-weight:bold;color:#bbbfc2}.breadcrumb li{font-weight:bold;color:#7a8288;text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.pagination ul{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.pagination ul>li>a,.pagination ul>li>span{font-weight:bold;color:#bbbfc2;text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-color:#3a3f44;background-image:-webkit-gradient(linear,0 0,0 100%,from(#52575c),color-stop(70%,#3a3f44),to(#3a3f44));background-image:-webkit-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:-moz-linear-gradient(top,#52575c,#3a3f44 70%,#3a3f44);background-image:-o-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-repeat:no-repeat;border-top:0;border-right:1px solid #2e3134;border-bottom:0;border-left:1px solid #52575c;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff52575c',endColorstr='#ff3a3f44',GradientType=0);-webkit-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';-moz-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)'}.pagination ul>li>a:hover,.pagination ul>li>span:hover{background-color:#3a3f44;background-image:-moz-linear-gradient(280deg,#272b30,#3a3f44);background-image:-webkit-linear-gradient(280deg,#272b30,#3a3f44);background-image:-o-linear-gradient(280deg,#272b30,#3a3f44);background-image:linear-gradient(280deg,#272b30,#3a3f44);background-repeat:repeat-x;border-left:1px solid transparent}.pagination ul>.active>a,.pagination ul>.active>a:hover,.pagination ul>.active>span,.pagination ul>.active>span:hover{color:#bbbfc2;background-color:#3a3f44;background-color:#43494f;background-image:-moz-linear-gradient(280deg,#30353b,#43494f);background-image:-webkit-linear-gradient(280deg,#30353b,#43494f);background-image:-o-linear-gradient(280deg,#30353b,#43494f);background-image:linear-gradient(280deg,#30353b,#43494f);background-repeat:repeat-x;border-left:1px solid transparent}.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>span,.pagination ul>.disabled>span:hover{background-color:#52575c;background-image:-webkit-gradient(linear,0 0,0 100%,from(#7a8288),color-stop(70%,#52575c),to(#52575c));background-image:-webkit-linear-gradient(#7a8288,#52575c 70%,#52575c);background-image:-moz-linear-gradient(top,#7a8288,#52575c 70%,#52575c);background-image:-o-linear-gradient(#7a8288,#52575c 70%,#52575c);background-image:linear-gradient(#7a8288,#52575c 70%,#52575c);background-repeat:no-repeat;border-top:0;border-right:1px solid #2e3134;border-bottom:0;border-left:1px solid #52575c;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff7a8288',endColorstr='#ff52575c',GradientType=0)}.pager li>a,.pager li>span{background-color:#3a3f44;background-image:-webkit-gradient(linear,0 0,0 100%,from(#52575c),color-stop(70%,#3a3f44),to(#3a3f44));background-image:-webkit-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:-moz-linear-gradient(top,#52575c,#3a3f44 70%,#3a3f44);background-image:-o-linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-image:linear-gradient(#52575c,#3a3f44 70%,#3a3f44);background-repeat:no-repeat;border:1px solid transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff52575c',endColorstr='#ff3a3f44',GradientType=0);-webkit-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';-moz-box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)';box-shadow:'0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1)'}.pager li>a:hover,.pager li>span:hover{background-color:#3a3f44;background-image:-moz-linear-gradient(280deg,#272b30,#3a3f44);background-image:-webkit-linear-gradient(280deg,#272b30,#3a3f44);background-image:-o-linear-gradient(280deg,#272b30,#3a3f44);background-image:linear-gradient(280deg,#272b30,#3a3f44);background-repeat:repeat-x;border:1px solid transparent}.pager .disabled a,.pager .disabled a:hover{background-color:transparent}.btn{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#484d51;*background-color:#3a3e41;background-image:-moz-linear-gradient(top,#52575c,#3a3e41);background-image:-webkit-gradient(linear,0 0,0 100%,from(#52575c),to(#3a3e41));background-image:-webkit-linear-gradient(top,#52575c,#3a3e41);background-image:-o-linear-gradient(top,#52575c,#3a3e41);background-image:linear-gradient(to bottom,#52575c,#3a3e41);background-repeat:repeat-x;border:1px solid #272b30;border-color:#3a3e41 #3a3e41 #161719;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff52575c',endColorstr='#ff3a3e41',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn:hover,.btn:focus,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{color:#fff;background-color:#3a3e41;*background-color:#2e3134}.btn:active,.btn.active{background-color:#222426 \9}.btn,.btn:hover{font-weight:bold;color:#fff;text-shadow:-1px -1px 1px rgba(0,0,0,0.3)}.btn-primary{color:#fff;color:#3a3f44;text-shadow:0 -1px 0 rgba(0,0,0,0.25);text-shadow:1px 1px 1px rgba(255,255,255,0.3);background-color:#bbbec2;*background-color:#bbbdc2;background-image:-moz-linear-gradient(top,#bbbfc2,#bbbdc2);background-image:-webkit-gradient(linear,0 0,0 100%,from(#bbbfc2),to(#bbbdc2));background-image:-webkit-linear-gradient(top,#bbbfc2,#bbbdc2);background-image:-o-linear-gradient(top,#bbbfc2,#bbbdc2);background-image:linear-gradient(to bottom,#bbbfc2,#bbbdc2);background-repeat:repeat-x;border-color:#bbbdc2 #bbbdc2 #93959e;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='#ffbbbfc2',endColorstr='#ffbbbdc2',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;background-color:#bbbdc2;*background-color:#aeb0b6}.btn-primary:active,.btn-primary.active{background-color:#a0a2aa \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#faa732;*background-color:#f89406;background-image:-moz-linear-gradient(top,#fbb450,#f89406);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fbb450),to(#f89406));background-image:-webkit-linear-gradient(top,#fbb450,#f89406);background-image:-o-linear-gradient(top,#fbb450,#f89406);background-image:linear-gradient(to bottom,#fbb450,#f89406);background-repeat:repeat-x;border-color:#f89406 #f89406 #ad6704;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='#fffbb450',endColorstr='#fff89406',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;background-color:#f89406;*background-color:#df8505}.btn-warning:active,.btn-warning.active{background-color:#c67605 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#da4f49;*background-color:#bd362f;background-image:-moz-linear-gradient(top,#ee5f5b,#bd362f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#bd362f));background-image:-webkit-linear-gradient(top,#ee5f5b,#bd362f);background-image:-o-linear-gradient(top,#ee5f5b,#bd362f);background-image:linear-gradient(to bottom,#ee5f5b,#bd362f);background-repeat:repeat-x;border-color:#bd362f #bd362f #802420;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='#ffbd362f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;background-color:#bd362f;*background-color:#a9302a}.btn-danger:active,.btn-danger.active{background-color:#942a25 \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#5bb75b;*background-color:#51a351;background-image:-moz-linear-gradient(top,#62c462,#51a351);background-image:-webkit-gradient(linear,0 0,0 100%,from(#62c462),to(#51a351));background-image:-webkit-linear-gradient(top,#62c462,#51a351);background-image:-o-linear-gradient(top,#62c462,#51a351);background-image:linear-gradient(to bottom,#62c462,#51a351);background-repeat:repeat-x;border-color:#51a351 #51a351 #387038;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='#ff62c462',endColorstr='#ff51a351',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;background-color:#51a351;*background-color:#499249}.btn-success:active,.btn-success.active{background-color:#408140 \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,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;background-color:#2f96b4;*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:#41454a;*background-color:#272b30;background-image:-moz-linear-gradient(top,#52575c,#272b30);background-image:-webkit-gradient(linear,0 0,0 100%,from(#52575c),to(#272b30));background-image:-webkit-linear-gradient(top,#52575c,#272b30);background-image:-o-linear-gradient(top,#52575c,#272b30);background-image:linear-gradient(to bottom,#52575c,#272b30);background-repeat:repeat-x;border-color:#272b30 #272b30 #050506;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='#ff52575c',endColorstr='#ff272b30',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;background-color:#272b30;*background-color:#1c1e22}.btn-inverse:active,.btn-inverse.active{background-color:#101214 \9}.caret{border-top-color:#fff}.table tbody tr.success td{background-color:#468847}.table tbody tr.error td{background-color:#b94a48}.table tbody tr.info td{background-color:#3a87ad}label,input,button,select,textarea,legend{color:#c8c8c8}legend,label{text-shadow:-1px -1px 0 rgba(0,0,0,0.3)}.input-prepend .add-on,.input-append .add-on{text-shadow:none;vertical-align:top;background-color:#52575c;border-top:1px solid #7a8288;border-right:1px solid #3a3f44;border-bottom:1px solid #3a3f44;border-left:1px solid #7a8288}.input-append .btn,.input-prepend .btn{padding:5px 14px;margin-top:-1px}.uneditable-input,input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{color:#bbbfc2}.form-actions{border-top:0}.dropdown-menu{-webkit-box-shadow:0 5px 5px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 5px rgba(0,0,0,0.2);box-shadow:0 5px 5px rgba(0,0,0,0.2)}.dropdown.open .dropdown-toggle{color:#bbbfc2;background-color:#3a3f44}.dropdown-submenu>a::after{border-left-color:#fff}.label,.alert{color:rgba(255,255,255,0.9);text-shadow:-1px -1px 0 rgba(0,0,0,0.3);-webkit-box-shadow:1px 1px 1px rgba(0,0,0,0.3);-moz-box-shadow:1px 1px 1px rgba(0,0,0,0.3);box-shadow:1px 1px 1px rgba(0,0,0,0.3)}.alert{background-color:#f89406;border-color:#f89406}.alert .alert-heading{color:rgba(255,255,255,0.9);text-shadow:-1px -1px 0 rgba(0,0,0,0.4)}.alert-success{background-color:#468847;border-color:#468847}.alert-error{background-color:#b94a48;border-color:#b94a48}.alert-info{background-color:#3a87ad;border-color:#3a87ad}.well,.hero-unit{-webkit-box-shadow:inset 1px 1px 1px rgba(0,0,0,0.5);-moz-box-shadow:inset 1px 1px 1px rgba(0,0,0,0.5);box-shadow:inset 1px 1px 1px rgba(0,0,0,0.5)}.thumbnail,a.thumbnail:hover{border:1px solid #1c1e22}.progress{background-color:#202328;background-image:-moz-linear-gradient(top,#202328,#202328);background-image:-webkit-gradient(linear,0 0,0 100%,from(#202328),to(#202328));background-image:-webkit-linear-gradient(top,#202328,#202328);background-image:-o-linear-gradient(top,#202328,#202328);background-image:linear-gradient(to bottom,#202328,#202328);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff202328',endColorstr='#ff202328',GradientType=0);-webkit-box-shadow:inset 1px 1px 1px rgba(0,0,0,0.5);-moz-box-shadow:inset 1px 1px 1px rgba(0,0,0,0.5);box-shadow:inset 1px 1px 1px rgba(0,0,0,0.5)}.footer{border-top:1px solid #1c1e22}.footer p{color:#c8c8c8}.modal{background-color:#2e3236}.modal-header{border-bottom:0}.modal-body{border-bottom:1px solid #1c1e22}.modal-footer{background-color:#272b30;border-top:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}@media(max-width:979px){.navbar .brand{border-right:0}}@media(max-width:768px){div.subnav .nav>li+li>a{border-top:1px solid transparent}}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}
{"name": "Angular Boot Camp",
"id": 1,
"date": "1/1/2013",
"time": "10:30 am",
"location": {
"address": "Google Headquarters",
"city": "Mountain View",
"province": "CA"
},
"imageUrl": "http://pascalprecht.github.com/slides/angularjs-insights/img/angularjs-logo.png",
"sessions": [
{
"id": 1,
"name": "Directives Masterclass",
"creatorName": "Bob Smith",
"duration": 1,
"level": "Advanced",
"abstract": "In this session you will learn the ins and outs of directives!",
"upVoteCount": 0
},
{
"id": 2,
"name": "Scopes for fun and profit",
"creatorName": "John Doe",
"duration": 3,
"level": "Introductory",
"abstract": "This session will take a closer look at scopes. Learn what they do, how they do it, and how to get them to do it for you.",
"upVoteCount": 0
},
{
"id": 3,
"name": "Well Behaved Controllers",
"creatorName": "Jane Doe",
"duration": 2,
"level": "Intermediate",
"abstract": "Controllers are the beginning of everything Angular does. Learn how to craft controllers that will win the respect of your friends and neighbors.",
"upVoteCount": 0
}
]
}
{"id":2,"creator":"jmcooper","name":"Code Camp","date":"03/15/2013","time":"9:00am - 5:00pm","location":{"address":"123 Wall St","city":"New York","province":"NY"},"imageUrl":"http://blog.laptopmag.com/wpress/wp-content/uploads/2012/08/Code-Camp_sf.jpg","sessions":[{"id":1,"upVoteCount":0,"creator":"jmcooper","name":"How To Program","track":"DevOps","duration":1,"abstract":"this session will teach you to program","creatorName":"Jim Cooper","level":"advanced"},{"id":2,"upVoteCount":0,"creator":"jmcooper","name":"How To Dance","track":"Web","duration":4,"abstract":"this session will teach you to dance","creatorName":"Jim Cooper","level":"introductory"},{"id":3,"upVoteCount":0,"creator":"bob","name":"How To Sing","track":"DevOps","duration":1,"abstract":"this session will teach you to sing Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum Lorum Ipsum FooBarsum ","level":"advanced","creatorName":"Bob"},{"duration":2,"name":"How To Be Awesome","abstract":"this course will make me rich","creator":"bob","creatorName":"Bob","id":4,"upVoteCount":0,"track":"Web","level":"introductory"},{"duration":2,"track":"Web","name":"How to build Angular Applications","abstract":"This session is all about angular applications","creator":"bob","creatorName":"Bob","upVoteCount":0,"id":5,"level":"intermediate"}]}
{"id":3,"creator":"joeeames","name":"Code Retreat","date":"03/16/2013","time":"8:00am - 4:30pm","location":{"address":"42 Wallaby Way","city":"Sydney","province":"AU"},"imageUrl":"http://api.ning.com/files/2vzV35vB8k5SPm92v9bGA0ng9DP2h-ONIcgPoDUThiIk1roHMF4eVjzlN2o3z9S5aU*wKgn9jZpdxEekNQCYvnOumr-beV44/cnlogo.png"}
{"name":"fall code camp","date":"1/1/2010","time":"10am","location":{"address":"123 fake st","city":"riverton","province":"ut"},"imageUrl":"http://s3.favim.com/orig/39/funny-llama-lol-nonsense-pink-Favim.com-325386.jpg","creator":"jmcooper","id":4}
/* app css stylesheet */
.hoverwell:hover {
background-color: #52575c;
cursor: pointer;
}
select, textarea {
color: #52575c;
}
.well-title {
margin-top: 0px;
}
.session {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.votingWidget {
height: 64px;
padding: 7px;
}
.votingButton {
padding-left:6px;
cursor:pointer;
}
.calendar{
border: 1px solid #555;
width: 238px;
}
.calendar .row {
margin-left: 0px;
}
.calendar .row div{
float:left;
padding:2px;
width:30px;
text-align:center;
}
.calendar-nav-left {
float:left;
width: 20px;
padding:2px;
}
.calendar-nav-right {
float:left;
width: 20px;
padding:2px;
text-align:right;
}
.calendar .row .calendar-hdr {
padding:2px;
float:left;
width: 166px;
text-align:center;
}
.pointer {
cursor: pointer;
}
.date-picker {
position: absolute;
top: -7px;
left: 209px;
background-color: #272b30;
}
eventsApp.factory('calendarHelper', function () {
var monthNames = [ "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" ];
return {
incrementCalendarMonth: function(calendar) {
if (calendar.month === 11) {
calendar.month = 0;
calendar.year++;
} else {
calendar.month++;
}
},
decrementCalendarMonth: function(calendar) {
if (calendar.month === 0) {
calendar.month = 11;
calendar.year--;
} else {
calendar.month--;
}
},
getCalendarDays: function (year, month) {
var monthStartDate = new Date(year, month, 1);
var days = [];
for (var idx = 0; idx < monthStartDate.getDay(); idx++) {
days.push('');
}
for (var idx = 1; idx <= new Date(year, month+1, 0).getDate(); idx++) {
days.push(idx);
}
return days;
},
getMonthName: function(monthNumber) {
return monthNames[monthNumber];
}
}
});
<div class="date-picker" ng-show="showDatePicker">
<div class="calendar no-select">
<div class="row calendar-nav">
<div class="calendar-nav-left pointer" ng-click="previousYear()"><i class="icon-chevron-left icon-white"></i></div>
<div class="calendar-hdr">
{{calendar.year}}
</div>
<div class="calendar-nav-right pointer" ng-click="nextYear()"><i class="icon-chevron-right icon-white"></i></div>
</div>
<div class="row calendar-nav">
<div class="calendar-nav-left pointer" ng-click="previousMonth()"><i class="icon-chevron-left icon-white"></i></div>
<div class="calendar-hdr">
{{calendar.monthName}}
</div>
<div class="calendar-nav-right pointer" ng-click="nextMonth()"><i class="icon-chevron-right icon-white"></i></div>
</div>
<div class="row">
<div>Sun</div>
<div>Mon</div>
<div>Tue</div>
<div>Wed</div>
<div>Thu</div>
<div>Fri</div>
<div>Sat</div>
</div>
<div class="row">
<div class="pointer" ng-repeat="day in days track by $index" ng-click="selectDate(day)">{{day}}</div>
</div>
</div>
</div>
'use strict';
eventsApp.directive('datePicker', function () {
return {
restrict: "E",
templateUrl: "/templates/directives/datepicker.html",
controller: function($scope, calendarHelper) {
$scope.showDatePicker = false;
$scope.calendar = {
year: new Date().getFullYear(),
month: new Date().getMonth(),
monthName: calendarHelper.getMonthName(new Date().getMonth())
};
$scope.days = calendarHelper.getCalendarDays(new Date().getFullYear(), new Date().getMonth());
$scope.nextMonth = function () {
calendarHelper.incrementCalendarMonth($scope.calendar);
$scope.calendar.monthName = calendarHelper.getMonthName($scope.calendar.month);
$scope.days = calendarHelper.getCalendarDays($scope.calendar.year, $scope.calendar.month);
}
$scope.previousMonth = function () {
calendarHelper.decrementCalendarMonth($scope.calendar);
$scope.calendar.monthName = calendarHelper.getMonthName($scope.calendar.month);
$scope.days = calendarHelper.getCalendarDays($scope.calendar.year, $scope.calendar.month);
}
$scope.nextYear = function () {
$scope.calendar.year++;
$scope.days = calendarHelper.getCalendarDays($scope.calendar.year, $scope.calendar.month);
}
$scope.previousYear = function () {
$scope.calendar.year--;
$scope.days = calendarHelper.getCalendarDays($scope.calendar.year, $scope.calendar.month);
}
$scope.selectDate = function (day) {
$scope.element.val(($scope.calendar.month + 1) + "/" + day + "/" + + $scope.calendar.year);
$scope.showDatePicker = false;
}
},
link: function(scope, element, attrs, controller) {
var forElement = angular.element("#" + attrs.for);
scope.element = forElement;
forElement.on('focus', function() { scope.$apply(function() { scope.showDatePicker = true;}); });
angular.element("body").on('click', function() { scope.$apply( function() { scope.showDatePicker = false; }) });
forElement.on('click', function(event) { event.stopPropagation(); });
angular.element(".calendar-nav").on('click', function(event) { event.stopPropagation(); } ); //keep datepicker open
}
};
});
<html lang="en" ng-app="eventsApp">
<head>
<meta charset="utf-8">
<title>Event Registration</title>
<link rel="stylesheet" href="/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/css/app.css"/>
</head>
<body>
<div class="container-fluid" ng-controller="EditProfileController">
<form name="profileForm">
<div class="row-fluid">
<div class="span3">
<gravatar email="{{user.emailAddress}}"/>
</div>
<div class="span3">
<fieldset>
<label for="userName">User Name:</label>
<input focus id="userName" required ng-pattern="/^[-A-Za-z0-9]{2,20}$/" type="text" placeholder="Username" ng-model="user.userName" />
<label for="password">Password:</label>
<input id="password" required type="password" placeholder="Password" ng-model="user.password" />
</fieldset>
<div style="margin-top:15px">
<fieldset>
<label for="name">Name:</label>
<input id="name" type="text" placeholder="Name" ng-model="user.name" />
<label for="emailAddress">Email Address:</label>
<input id="emailAddress" type="email" placeholder="Email Address" ng-model="user.emailAddress" />
</fieldset>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span6 offset3">
<label for="bio">Bio:</label>
<textarea id="bio" rows="6" style="width:97%; color:#52575c" placeholder="Bio" ng-model="user.bio"></textarea>
</div>
</div>
<div class="row-fluid">
<div class="span6 offset3">
<div class="pull-left"><span class="btn">Cancel</span></div>
<div class="pull-right"><button type="submit" class="btn btn-primary">Save</button></div>
</div>
</div>
</form>
</div>
<script src="/lib/jquery.min.js"></script>
<script src="/lib/angular/angular.js"></script>
<script src="/lib/angular/angular-resource.js"></script>
<script src="/lib/angular/angular-route.min.js"></script>
<script src="/lib/underscore-1.4.4.min.js"></script>
<script src="/js/app.js"></script>
<script src="/js/controllers/EditProfileController.js"></script>
<script src="/js/services/GravatarUrlBuilder.js"></script>
<script src="/js/directives/gravatar.js"></script>
<script src="/js/filters.js"></script>
<script src="/lib/bootstrap.min.js"></script>
</body>
</html>
'use strict';
eventsApp.controller('EditProfileController',
function EditProfileController($scope, gravatarUrlBuilder) {
$scope.user = {};
$scope.getGravatarUrl = function(email) {
return gravatarUrlBuilder.buildGravatarUrl(email);
}
}
);
'use strict';
eventsApp.factory('gravatarUrlBuilder', function() {
return {
buildGravatarUrl: function(email) {
var defaultGravatarUrl = "http://www.gravatar.com/avatar/000?s=200";
var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!regex.test(email))
return defaultGravatarUrl;
var MD5=function(s){function L(k,d){return(k<<d)|(k>>>(32-d))}function K(G,k){var I,d,F,H,x;F=(G&2147483648);H=(k&2147483648);I=(G&1073741824);d=(k&1073741824);x=(G&1073741823)+(k&1073741823);if(I&d){return(x^2147483648^F^H)}if(I|d){if(x&1073741824){return(x^3221225472^F^H)}else{return(x^1073741824^F^H)}}else{return(x^F^H)}}function r(d,F,k){return(d&F)|((~d)&k)}function q(d,F,k){return(d&k)|(F&(~k))}function p(d,F,k){return(d^F^k)}function n(d,F,k){return(F^(d|(~k)))}function u(G,F,aa,Z,k,H,I){G=K(G,K(K(r(F,aa,Z),k),I));return K(L(G,H),F)}function f(G,F,aa,Z,k,H,I){G=K(G,K(K(q(F,aa,Z),k),I));return K(L(G,H),F)}function D(G,F,aa,Z,k,H,I){G=K(G,K(K(p(F,aa,Z),k),I));return K(L(G,H),F)}function t(G,F,aa,Z,k,H,I){G=K(G,K(K(n(F,aa,Z),k),I));return K(L(G,H),F)}function e(G){var Z;var F=G.length;var x=F+8;var k=(x-(x%64))/64;var I=(k+1)*16;var aa=Array(I-1);var d=0;var H=0;while(H<F){Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=(aa[Z]|(G.charCodeAt(H)<<d));H++}Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=aa[Z]|(128<<d);aa[I-2]=F<<3;aa[I-1]=F>>>29;return aa}function B(x){var k="",F="",G,d;for(d=0;d<=3;d++){G=(x>>>(d*8))&255;F="0"+G.toString(16);k=k+F.substr(F.length-2,2)}return k}function J(k){k=k.replace(/rn/g,"n");var d="";for(var F=0;F<k.length;F++){var x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x)}else{if((x>127)&&(x<2048)){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128)}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128)}}}return d}var C=Array();var P,h,E,v,g,Y,X,W,V;var S=7,Q=12,N=17,M=22;var A=5,z=9,y=14,w=20;var o=4,m=11,l=16,j=23;var U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g)}var i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase()};
return 'http://www.gravatar.com/avatar/' + MD5(email) + ".jpg?s=200&r=g";
}
}
})
<html lang="en" ng-app="eventsApp">
<head>
<meta charset="utf-8">
<title>Event Registration</title>
<link rel="stylesheet" href="/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/css/app.css"/>
</head>
<body>
<div class="container-fluid" ng-controller="EditProfileController">
<form name="profileForm">
<div class="row-fluid">
<div class="span3">
<img src="/img/profile.jpg"/>
</div>
<div class="span3">
<fieldset>
<label for="userName">User Name:</label>
<input focus id="userName" required ng-pattern="/^[-A-Za-z0-9]{2,20}$/" type="text" placeholder="Username" ng-model="user.userName" />
<label for="password">Password:</label>
<input id="password" required type="password" placeholder="Password" ng-model="user.password" />
</fieldset>
<div style="margin-top:15px">
<fieldset>
<label for="name">Name:</label>
<input id="name" type="text" placeholder="Name" ng-model="user.name" />
<label for="emailAddress">Email Address:</label>
<input id="emailAddress" type="email" placeholder="Email Address" ng-model="user.emailAddress" />
</fieldset>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span6 offset3">
<label for="bio">Bio:</label>
<textarea id="bio" rows="6" style="width:97%; color:#52575c" placeholder="Bio" ng-model="user.bio"></textarea>
</div>
</div>
<div class="row-fluid">
<div class="span6 offset3">
<div class="pull-left"><span class="btn">Cancel</span></div>
<div class="pull-right"><button type="submit" class="btn btn-primary">Save</button></div>
</div>
</div>
</form>
</div>
<script src="/lib/jquery.min.js"></script>
<script src="/lib/angular/angular.js"></script>
<script src="/lib/underscore-1.4.4.min.js"></script>
<script src="/js/app.js"></script>
<script src="/js/controllers/EditProfileController.js"></script>
<script src="/js/filters.js"></script>
<script src="/lib/bootstrap.min.js"></script>
</body>
</html>
'use strict';
eventsApp.controller('EditProfileController',
function EditProfileController($scope) {
$scope.user = {};
$scope.getGravatarUrl = function(email) {
var defaultGravatarUrl = "http://www.gravatar.com/avatar/000?s=200";
var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!regex.test(email))
return defaultGravatarUrl;
var MD5=function(s){function L(k,d){return(k<<d)|(k>>>(32-d))}function K(G,k){var I,d,F,H,x;F=(G&2147483648);H=(k&2147483648);I=(G&1073741824);d=(k&1073741824);x=(G&1073741823)+(k&1073741823);if(I&d){return(x^2147483648^F^H)}if(I|d){if(x&1073741824){return(x^3221225472^F^H)}else{return(x^1073741824^F^H)}}else{return(x^F^H)}}function r(d,F,k){return(d&F)|((~d)&k)}function q(d,F,k){return(d&k)|(F&(~k))}function p(d,F,k){return(d^F^k)}function n(d,F,k){return(F^(d|(~k)))}function u(G,F,aa,Z,k,H,I){G=K(G,K(K(r(F,aa,Z),k),I));return K(L(G,H),F)}function f(G,F,aa,Z,k,H,I){G=K(G,K(K(q(F,aa,Z),k),I));return K(L(G,H),F)}function D(G,F,aa,Z,k,H,I){G=K(G,K(K(p(F,aa,Z),k),I));return K(L(G,H),F)}function t(G,F,aa,Z,k,H,I){G=K(G,K(K(n(F,aa,Z),k),I));return K(L(G,H),F)}function e(G){var Z;var F=G.length;var x=F+8;var k=(x-(x%64))/64;var I=(k+1)*16;var aa=Array(I-1);var d=0;var H=0;while(H<F){Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=(aa[Z]|(G.charCodeAt(H)<<d));H++}Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=aa[Z]|(128<<d);aa[I-2]=F<<3;aa[I-1]=F>>>29;return aa}function B(x){var k="",F="",G,d;for(d=0;d<=3;d++){G=(x>>>(d*8))&255;F="0"+G.toString(16);k=k+F.substr(F.length-2,2)}return k}function J(k){k=k.replace(/rn/g,"n");var d="";for(var F=0;F<k.length;F++){var x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x)}else{if((x>127)&&(x<2048)){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128)}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128)}}}return d}var C=Array();var P,h,E,v,g,Y,X,W,V;var S=7,Q=12,N=17,M=22;var A=5,z=9,y=14,w=20;var o=4,m=11,l=16,j=23;var U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g)}var i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase()};
return 'http://www.gravatar.com/avatar/' + MD5(email) + ".jpg?s=200&r=g";
}
}
);
'use strict';
eventsApp.factory('gravatarUrlBuilder', function () {
return {
buildGravatarUrl: function(email) {
var defaultGravatarUrl = "http://www.gravatar.com/avatar/000?s=200";
var regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if (!regex.test(email))
return defaultGravatarUrl;
var MD5=function(s){function L(k,d){return(k<<d)|(k>>>(32-d))}function K(G,k){var I,d,F,H,x;F=(G&2147483648);H=(k&2147483648);I=(G&1073741824);d=(k&1073741824);x=(G&1073741823)+(k&1073741823);if(I&d){return(x^2147483648^F^H)}if(I|d){if(x&1073741824){return(x^3221225472^F^H)}else{return(x^1073741824^F^H)}}else{return(x^F^H)}}function r(d,F,k){return(d&F)|((~d)&k)}function q(d,F,k){return(d&k)|(F&(~k))}function p(d,F,k){return(d^F^k)}function n(d,F,k){return(F^(d|(~k)))}function u(G,F,aa,Z,k,H,I){G=K(G,K(K(r(F,aa,Z),k),I));return K(L(G,H),F)}function f(G,F,aa,Z,k,H,I){G=K(G,K(K(q(F,aa,Z),k),I));return K(L(G,H),F)}function D(G,F,aa,Z,k,H,I){G=K(G,K(K(p(F,aa,Z),k),I));return K(L(G,H),F)}function t(G,F,aa,Z,k,H,I){G=K(G,K(K(n(F,aa,Z),k),I));return K(L(G,H),F)}function e(G){var Z;var F=G.length;var x=F+8;var k=(x-(x%64))/64;var I=(k+1)*16;var aa=Array(I-1);var d=0;var H=0;while(H<F){Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=(aa[Z]|(G.charCodeAt(H)<<d));H++}Z=(H-(H%4))/4;d=(H%4)*8;aa[Z]=aa[Z]|(128<<d);aa[I-2]=F<<3;aa[I-1]=F>>>29;return aa}function B(x){var k="",F="",G,d;for(d=0;d<=3;d++){G=(x>>>(d*8))&255;F="0"+G.toString(16);k=k+F.substr(F.length-2,2)}return k}function J(k){k=k.replace(/rn/g,"n");var d="";for(var F=0;F<k.length;F++){var x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x)}else{if((x>127)&&(x<2048)){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128)}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128)}}}return d}var C=Array();var P,h,E,v,g,Y,X,W,V;var S=7,Q=12,N=17,M=22;var A=5,z=9,y=14,w=20;var o=4,m=11,l=16,j=23;var U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g)}var i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase()};
return 'http://www.gravatar.com/avatar/' + MD5(email) + ".jpg?s=200&r=g";
}
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event Registration</title>
<link rel="stylesheet" href="/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/css/app.css"/>
</head>
<body>
<div class="container">
</div>
</body>
</html>
h( L�@j�A�N�=����[�=�2�9����Kp�L�;�;�H�d�����J����b��9�W�W�F�"d�Y��H�7����U�D�]�����I�����F�5�S�8u�B�u��%g�D�q��Q�@����^�@����o��B�O�>�En�K�3�@�_��-m�<�_��V�2p�:�G�"e�I����8�E�L����G�6����*W�C�!a�P�E�R�A�N�6�C����P�?�QN.Y= FK%9J+N(:Y6=A U%D$Q4S.6=AF5T 8 +N1Y=&-P L!M$GV(SY6&R;LLLI3HBQ4S0?>LLLLL#7J+CCOLLLLLL!)XRCCCCLLLLLLWCCCCCCCLLLLE<CCCCCCCCLL2"CCCCCCCCCCL/,*CCCCCCCCCCC'
@CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
�PNG

IHDR����C| pHYsaa�?�i IDATx���y|��?��{�Wˈ#7���)W� �h�������uw�=���Ǯ]��]�ݶb��z./T��&��$��~������IL��d&�=���f!����-��{>���C� !�B!�"�8�N@t��q�Ʃ���`�k��#.�k�ܹs��NN!�B!�@2��6nܘ�T�п�Sj��p8
l��˫�� �B!��>R손���_ED7���k���*�SeMtt�����~�B!�B�I��6o�����]OD+�y� !ˉ�e"*X�`�6� 1�B!�"�H���nݚTSS���W0�L��!�S^b������� B!�B�� �n�غukTuu�7X�̞N�(��
���w��B!��TR���QXX8��W�RfN�;! �D���(���/�;!�B!��K�]�_�~��(+TU�@��"������������|�B!�B )v{�ƍ�7�71� ��1��>P�T��;!�B!���ڲe��>��p����,D�����(N��s瞶;'!�B!�hM�]����;����6�Ý��dwN ��DT�̯/\��� !�B!��&(**�9}��"UUW�cf��9ف�|6QAbb��ɓ'_�;'!�B!Dd�bנ��"��ӧg7�K�9�ք��V]� �����d�^�zm�4iR�� !�B!"����ƍ�VUu3���� 9Ϝ��d\��\t<*��6�!��HL�;�fU������~�����z}v'$�B!�oR�j�aÆQM#�+ �;G��J������-�Ϧ�ù�GZ>w]v9<CG�3xXЌ��y�E)���{@�B!��G��Nlٲe`CCÍn0��|����}����Ɏw�i_��<Q�F�}�P(�qV���i"Z�`�…����B!�BSH��ʖ-[z����̼����;�|p:���p9b���_��mF�׀ x�����!P�c���0":���o���!�B!B[��}�Q�� �m� w.�� �*\Ǐ��7\_��4_���mCQ��Ը�7s(��1�����+f~��r̛7o���!�B!BOD�{��u:th~S�{ �h�sr���{�nx| ��6Cw��
9p�
�a#�4�v�c6"� `53������|�B!��!��]e���ӈh3/`{�b��<�%��.�G��n��N��9]pg�g��e����m0( �� ,���B!�"l�}��y���߿�������Pm ���]�β��>Y��ojLr�����a#�ʸ�w�7��� EY�`��Sv�$�B!�.aY�_��*"����y����������ѮMሉ����·~jz�f���3xx��E��^:��S����:k֬�.�B!�B���)v7o�����]�ƭ��ٝ�
��Cp����~��g��z_��N���� ;�of�D��3d<CG™>�/�l!�"z=//��B!���b���Nlhh��i���l�������dܥ_B��陛* {z�:S��S8�o��}�oϐ��  �e���;CD�
��۷������B!�"�\��u�֨����M��2����8.���d���@1�є^�Sg���� ���CYY�^y K�]���߸���p��̖�#�jf~���(��9//��B!��
�bױaÆ�M�u̜`wBJM5����d7�Oښ�UO�7�e����N�B����z�֜���ϰ��6��>v� �
f~��pDGG�����;'!�B!�����ݰa�UUW������nn4u�0���hJ���1�O@K��(
.<�;ԗO�bGr
<�F!j�8z�ٝN��D���
,X� �ͮ�B!�a �����a̼�i?�Av�U������p��5��㇐����.�TU���OٙZ������:�#����4;AD/7��ٝ�B!��{���ݲe��>�i��v�46�r�솻t��M��韎!�����[�����$|��ݐ�շ�CF�3t�Iv� ���\� ??���!�B!����ݼys���_�4�;��ɖDZq�/�{��� v�ӥ���Gʬ���[�}� G���������W���;�f%����t:�̝;w���!�B!���b���(�ԩS�4����c7�R]��=�� ���Y��GB"�?���n9��~�<��fY.�'�|��`G�������w�0(�qv� ��DT�T��;!�B!D�,/v�n�ꬪ��KD+,f�XKo�54���>����� �r������g��m�����C8^��L�T�(p^��T��cwJ"� @3�������|�B!�mYU�ҦM�&�̼@�7�%hi4�:T
W�)=����g^�#!����]���א,�AQ�θQ�F9�}�e3����_����؝�B!���bwÆ ���&f�����cp����/A�uv�c���yH���oo?�����/p��Wz"�C܃2�:�+���/�^�;����ļ���� ��B!�S�.v�l�2����F7aJV��(?Ӹw�(���C����O���Ί�(���מH��t�}�U� ��W���/�"�DD111o���Vٝ�B!���P��q��4f���W0��䥛R]ոw�n8�O۝�e����;�Zg�.��~��޵2��@n7ܙC ߁� ��������&66vCnnnxL1B!�"�i.v?�������M{��ff���u���.�w�8O �u�]���G��Q~�R�n����㿰2��C<��5Nu�b �(v����_�~oy�^�� !�B�.Y��ݻ�}��ἦ�E�{.��Q ב� ��@���)���A���������':�z��7Q��؊Ԃ���a� ��W����%�s��(JA^^��h\�+�B!�0AGŮRXX8�i/ܥ̜lGb�9�>
w����6���p���4%�ӯwU�F�}� 2 -Jl<CG|]�A�G ʈhmS��1�� �B!��Z����B/�o���/�5�&��3p��{�^(U��hJW�4 {z�t���bj���_09�Х��#j�Hx����_��iv���$%%=4y��v'#�B!D(����+TU� `����RU ���p�솣\�,m��ۑ�h�%�)--��_}�sb��p�?�03���HL�g�x����w_���,\���y!�B����AUU[ ]�����M}}��T��#:)���6*�>�P_�q#�H�����>B�g��� �a#�:���v�S+��B!��3Saaa-3{z��?��K�)�ב��j4�Wڢ��w��]��ed�/V���~cFj����#5l$�)=rO"ڽp�Ž�n !�B!����| @��wcnj4��J@ ���2�)
z�]��T"�t^Cb2� �WF�:h��gOÿ�4j��g�~�6�!��HL��G� .�B!D�k�+�,,vg�;)����ʪۄ��IS�N3wm@U�w�
�S�F�IT����g�˚�[��o��d>�B!���":r��v�P.V³\%��8w��ؑ$5��M��
����Z/��F�O~���_����p�hln5d8��83��ȮB!���zd�ۨ���{�9�����b��DL�`Kb�*�,^��k�bI�H�;~��GQ��f�.πg���
%:�hH)v�B!����~?܇����є����@j[ u  (�����U G���A`K!�W �g�Hx�
�h��p8��B!����]}���:~��{����FS���� �c'�Fk��f�@}\�����Nh��h8T��C���p f�d�N���R)v�B!����W]��c���Kp�K
]���_�,^�p�ʶ�@ ���|��9sʬLG!�B�p�@߾}��<���-KH��$���{�T�L��#��t�ƭ����xZ!�B�nP������֋�t��H�������N��c��dΤd���f!�B!�Ii�{��nTO����˅�����=�$f���{F"%A�)v�B!��&c�.5.��T$O����fJ�5���i�m��,ŮB!���R��}��x)v�����k�vcn�Vq �Jk��9]Pb��(��B!�BtSK���[�b�t 9��I`��S��v�p�о^����
L!�B� -Ů����e��R-�����(x���5�p�$j�`�ԍY!�B� -Ů�咑]E]1q#G۝�/�;���H�>����𵅩!�BZ�]��# �l�vM�Gu��f��/)N��B��=vON�4���t�B!�{-�nnnn��z�/�Y\)��49��4������;Z��N�B!�B����C����8���D�.X r8�N����2��l*� ��H�S !�Ba����G�jL���D %*)���N�������ev�VddW!�B��զ�ջ�'h[�(:�2k�q��2c�nK��WA4����-{�
!�������!�)����ś�JR�.Xlw�*��/�;������ ���B���5v'!�O�]�k�%N� w�v��)��1�.�Z;17�bW!D$Y��!����Ȯ�ݓ���� �f�4f�1#y� ScF"������+�""����NDc��G~��D��؍�bר�!�s�P���R��)v��Nc&��y��i��K!�E���?,))��@��tv�Bզ����;�F��2�k\�kB��q��c�N#�i��,���BD'��裏��%���HD���K�:�~�y�.�Ȯ!����0n��ih�8s��)�4E{�r)v�B��������M�6����nw۔�"L}���3�Y����4���_gI�'���6�w8�9ؒؑ�����ջ�@!�A����`�ƍ�p����z<#!D��hdW�TY����9v��[��ڝBH"� ��t)v�B��;vL0��7�DUUUG����g�B��n����ӬR��C�x,�����0E}T ��.�,~�r$i�c��tm�%�B��@ ���QPPp����|��[kv�Ճ�N��_dIl��q�/���$>�]M���Cc'�&2�+�",m߾�
K>��C;v�R��"��=��"��4��<u&��)�ľ����/? ����_b���¢��o��B!’���p�^�Z��+-NG!�Q�z<]܁�x� s���Y�|����V�iQ�0fF�%7X;\i��LD� ��8!����ݻ7��n;p�>��s-�, "�)��o�y�={�ׅ��~f�4V��4��EԀ Kb�|�5�%@j?�.^��� �I֌ ��EE��j-�nt���Z� ���Ϙ�Š|��H�q�f�D�`(�����H���i�{�z4��^��@���5�z�1f>�S�]�BD����2�[V� �( ��z���> rI0A�e����ٹ}�����IGu�иv���ee??{��P�����#�ApՌIP�����緼�Y�U9YeՌ�}Y� ��AjA� �3zq
3%��&��nf���:��E�1��:j� �SPpJ!:I�)Uq�T�q���ys�Y�wT�| �
-�xiP�Eڢ���.��F��O�u5R��@��{���>�.é5/�;i����V6�Z �v�׼ID�23[�P���:�_
��r�qM#(��0���� ���k�:U�`{��/lx��7�:ί���z��'�
����6��_<lA.B;���;w�6m����"�)3_�*� ��gO� " �����}��+^vRK���kT�c.��~A�z��A�LR��D�eF6���RV�z2l|J���k�5�cZ_��Nh��Mc<jc񉦛��sxn��� ������^r��~g��C��/t u6z{�]�i�]�����Kb.V����9V_^��
�c�,�'2��\`_�/�6Ә�m��"4>�>aw"�|D��L�̂���,�h,�Ƿ: �}>G���f>cGrB�oߞ�̙�֭�O�sC,�;��J_l�����"*���e�����wל:2��W��`d6����c,�� ���V���Z~�p1�f�GN(|��O��]��Z��.3k~�f�������V��{{c�Eg�__B�w�?36��^s�l�k��'�v�<QZO7��-))����u@TT����п�G��#f�ffn����4���J������v�d)"��[n0��t�p���������cf��Ziii�Z�����Z�ݻ7��v+�v�� ��RZZꩨ���A��<xж\�1���lhh��u�\~���փEiiiBEEa��� `b�ǯ��5�63[�\��Š�ȇ���؜�q/̘06�����)�������`d38�o������c�C�?̠�������ǹ��td�39ISfX\UqvKa�_�=y1u��#t� (������UUU}���������0Nk�(��܂EEEš���ƍ��\�BD��6N�/�F�����8aFЊ����&�G��jF�PWWW�����3g�ڕ����.�����m;s�g��y�f�;w�H��Ѹl$lTVVA���p��ӊ� h|A����ڜOw�Bӟ]� �9CVM��`�����ߘ��[��'1RX̪�kV���U��^u��Ϊ]�Y��*%-�Z�E{�V~�)|g;�s��u "K��g iJ�%�Å�c�&=�)zX��H�l�"rѷ��*�k��6:��!����1UGd��0
��D�i��&������VM�9s���'ڝ��qu�-ϭ>�:�{��3GGO��,�,��Mo\��U��Bl��u�1�X;�ٓ���lP�]����$Dך������tu�B��ڵ�7�o}���8p�@wBM$"�]�Epr���N"ʶ;�H�:j������[�;;tX��m�#�;�<c.���?qU_�����֭��!>g|�'F(��.��(_[�Nw����}�UHk�"�S4�����t!����ᅴ�=/����0#���ED��N$��0s҄�ʺ�̸��E�����D]�L����&"�.\lY��_�t^�=��p�y|��?��1��D^^^����@A�t��v'"�""�
@�P_%����f��رcض͔�K�����\�l!�v'����]�����E���I�&��|1h;2����O���5��5���;�Ͽ�I+3��.� �s�1�9X�붗�ƆU�,B�5�߁ƦM��"�
���+�)((0k�'�{�$�J:Gxò�]��u��T�������RGG��D�����R]gY� ᅪ��v�v!#�T����<?5���$v(S®��)��v'�(���0s��m���EZZRSS[>�����x�v�[>�~?jkkQ[[������WUU�ĉ8~�8*++{*m!Dϻ���
o�q��":�ND2s���":2�Sn�;�pPp� �>�Ϭ���r����>p��Aq{@.7����6�������� -����p�s���Z�n��.afM[Tp\<@غ�p��< �C�[��7u_S��; ��@j����:N�$~("�J��f�A܉�3���>`f��)�?ѿ3�e]����1c� 33��woM�?5�x�"�?��q���ܹeee��C�󊋋g2���^{ ���>�&���f 5�>��e;mt���UUU-��޽{q��A�n����d殛шN>�����1��D\�� �����I�wJ�ƺ�$��4�����Y��5��P~5ǎ���/ƻ�ՄI���b�
i�X�%\ծ�up� IDAT������~�2r�ڢ��S-ʼng�ӒءH϶CAމ�#�y"�a��v'i����{UUo7;nTT&O�� &`���HOO7�m���cذa6��2�S�Naǎعs'v�؁��R��A
!z��@ ���+b�GD������GC���zl۶ ���*>���~�&O�gNv��� �v�ӏ~n�? Gt G�F��,���1!�K�/:=���\��p�5����!�9�����:_�\jd�����8>�b��}�8�j��w��Х�|�!0v�����/1 ����4>�8��b��KI�2Ma�����F����ӧ�gf̜����c֬Y�����<":�$�S�|��꘹��ꙹ����9��b�1s,�8)Dԗ�5��۷/�ϟ����h~�����;���O>Q�~���,D�*..�����éS�4]�����۷k�� X׌Dh��x0k�,̚5 G��#�<����KD��Y{S(..��T�k�*+������:}O��h �@DU���@5U1�@L�s@ ��_c�2s][��Sz��� I9�����G��>X��=�P}�.g�2� 4vdv� L��/k��|gO���3�ן~w L��Z�5��i�,é�oI�P�c�.\.W(�0�@��� �޽�3b)��y����nCFFF�/��������,IHH8���Qg�~Ǐ�.//���2��JW�a�K6&���G~~>���QQQ�ѬY��� �t��!,s?3+�W��trjj*{�1\s�5hh���t%"��%����Hd�4vM ``g'0�>�,^z�%<��S�� ��:l߾������⋝�c�Ӊ�鳐6s.1-/����\`�0s����u'ߢ��D��10dQ3l�렦��5&cb2!m�\�/V�⋝���UU�<\�1�� ��3z�q��!e�\��ݼP��dʷ�1ڤ�������A���$~(q$%k?�����?lZ���݉����﵍7^kF���<�~��0�e��/��mEQ�V�����j3��Zzzzmzz�{�m�Ν���dS�y2Gn:���`��HD1f�)�0���(��nݳgv�ڥ�%K� 55s��Aa���u6�d�݆� a999�n�}��ݛRWW7���`9 �D�+V��r�'�0z�YD�`f���ݻw_���s'^����θ8\� nP�
���� ���fVV�%���^ow��h}��k׮�>�o"��&ѰK�'q�'���S���G��я�(��WU,�J��R0uZ��Z)���9 �DE[�}>�{gS��,| ���:_�^�tHeF���8��_L�j���ѹٳgwoމ��@D;�y�݉������{�ۅnzz:~���a�ر� _ ��srrJ��ӨѣG���sPTT��(J.3/���q]'Y�:�3s�K/���dEQ�x�b��e���@����ug( >|�9�l.**�_Q��TU�%�6�]�/_�cǎA��~;�h�/��4��A �*ݟRs�����>x�~Oj�s����뵻���Td��(--M����&�tf��N��[�{���4}���]�p�����_�i[ 5.��]r8������]�h+&t$������NO�E� r{� ���:������ ���Df��|������׿��ʪ��͖���q�=� ::�]"z4;;�]A���������ÇG����'�e̼��܄�r�=eeex��5]0m�4���0j�( <��k~gz� 3�1��莦ѹ'v���������-w�y'6l؀ . ?R�v����zf�v��m����J��{�a�#!�'999_���i233+�����_|������z��t9�x�ťD�yt7ҋݤ)3�J�eY������r�up;/5�ݸ3R��Y;�( ��߇�z������I���۷����_|��.� =����񝨨��999�����A���edd�y���rrrnNLL��t:���TTTt3_���/ïqׇŋ����/_��Qh\�)l���U�t:g����cccq뭷 k�z�0�w�^73?��աr���n-�����?~���^o��5j�����?{���.�+MQ�eVQ��$�ꤤ�؍�i�i���,v́}�)5o�(������\t� 8"�]�긋�7c'� &���Dt���D����a���vժU���$''�駟._�p�Ҝ���^����� ���Y?z�h�?Յ�#��uuux��W�^�Ք)S~�������᪅��MDn=�eeeU+���`�� .4��n'����VTUZ���DwJ�?-�ׇ����zVVVVuvv�_�^�M999S:;ϴb��c�kF �]\V6��dY��M��6;���,��3�4u�%�C��N̊�]�;u�T�|��F/�"if>�����Q̼淿�m�j�!]||<���&L�pUNN�+&�(�-v��1���.,,DE���8��Q�7���h�R_7�@X#;;����h3�&99W]u��p��d�n���_꿒�Dr=��섂@�3ֺ*vu�'���ݴEK-�����O>0=�Z_ljc��m=n@��B�#Q{�� ��|�=�`�HC5k �uD������s��=�駟���� ]�w�}��w�qǒQ�F�79=!�h#<���ژ
@=�Ucƌ9 `C�/,]��yj�� �5ƌ�:ڭ�7n\'g_���t��I����1��PUz@��
�:oÆ�j�c��.p��˯@�����/{#؂��pr�KpZ5�K
⽆�Æ4EG��(JP6�r:�x��Ǒ�`����krJ���hF ��o~��!x�ܹ������̼��#;w� ��O>���z�Zf> D�t�/ 4^�WO
�D${n "��֟����h(��3_ ���?}J��*;4��
�,v���"�IUZ�uku��(�bݾ���p�-�,~�����fzFv�qs��}���4z��Dt���D��?������������-{0
!"����K�63-ku���78���˖-ӛ��� f~���I�w�hO��N�,�?�ێ�@����'Ŗ$��-v�#kց31 �S�+�*�� |��v�?�v5JW����.DaI�`���%���s��� S�N�-��b��� "]��#���ۿ {�ڵ������{��Iy� ��EEE1�8x� >��S���b��k4T"z�� 3f�@�^�v�����k�"4�z�'�i�<1�p]cJBa�� �d�?�����LO*\�ʙ3g�I Z��q��� � ��\��?�� �b7k8Q57��-y��b+�Ә��0B` ���YYYF.��q�]ïv#���ǣUU��ѣG�<4����Y��֬yB�vE�@����@�Q�fD���!t:�X�x��Tܧ�a"*i�}]]��0A=`����!h�W6P]��z�4;�P�Ր��.F�ԠJ�x�2�p[�.�?��=;-����u��X5�w�Kb+E� ����\^^����N��>���7��2#�pVVVv��u�� �,3�31%!��b��.\�� 6t}v�*/�?���}@�o~K�,��\�="�����V���ӧO��1j ���ց:�=����&"��N�g�@��MΝ���}O��6�/;���j��,���6��'
J��=v�((��u�֭�f��n�߽��~��������c���F�����f$���}����<�W^A}�����bG_P�M���}�b�ԩzR�p�� �5��l�� �~�*��ߵ.v٧y�m��)v;�}��H�%B��%�������t}��μ�d�VA��d8S�,�lIɚ�%����|���:׷�VS�L����j4�cDtuws G۷o�`�Ν;QYYi$�S�,S��=FUՕ~�/����˞�� cƌ���v�>f�QսDd�45����-������K�����������WA��>�-�4?����h�
e c'���2����ԺZ��w���!����%ҪW�u/����k�oP�����-&�ot��뮻0f�#!���tXlGU���~h�r?.�)�f۾}�ps�z�-�={���|�̻�8����ĉ����'��"g*Y�"�����cǎ�N�����(|0s\��#=�(���[���ջ5J$td�}����bh*c��[��e��w?(0�g�!q� ����Q�����w n'��/�����X۶m3�5f�ȆB{0�J0��Pg���^D� a�ҥzӓm�l���P\\��2C�[���(����ۭ�zR�vD��poR�91n�S��u_k� f��}{kQs`�+�Kb=Ә��.���~���>ֻwo<���FC��n'&�h����q��a#!�|�B��۷�3߼c�|��Z/+��jÇ�"�?�>�h�"��=�O%�=�]7n4r-(45�0��-�g3i9B�/6u�.��n�"kGu�7�ii��\�b��e`&(*ڲ��@�4f�O�>A�(`Ȑ!E�@��&M���n3� �Dd�ա%>��s#מ���d��Rjjj��sT�O̬�����l�,#)) s���s/x@��;v�H"����Z����FB�l�ε���7�`�hS
]�W\q�1"�<ܧ�qGfwZo$N�bY���e���������1�5��Tf$ϞoI�`�H����k��{��3f}c�;�99�^�+VQ�.� cǏ�f���o��]�62�jnVBѱ��"��O�8��[�j���^��YYY���M��˗kα� ����.��.gf�o�[TU�􍭩D- }��)�/&� <�`�-%��?x��� z޲�s���p `�~�P�e= �.�o�m�&��Y #��C�C�^�A?��������R�c��>�(����]�@9��/����D���:��&뚄=��n`��k֬��j~��3��O�FU#G�Đ!C�p��� �in���ϱvm���;r �4�+-��:A}�P�HZ�^��������M�y�Ncv��"y�<�⳯���lY|=�}� 1Nkj@���ĶEE��m����}����RSS��#�ݶj�G��-����8p�@W����bzRBѹ����x����\��x�x<�Q�=k lCt'��� A���h@mm�z�h�_2s]קE���Gr8}��0�fS3
��]=�j\��l�X�9 ����,��m[�hh�MKTb�3�&�c'Y:Bn���!W�65������ ����h�&���N.1�X8t�to�U���g%��TTT4���o�����_�݈d��� ���������J�m���UVV��ʕ+�� C�H���)��6ώ�2 ��;��>^�´���ڍ��BFq�W޵�����T�}o �,� (
b��-�m'%!��]���٥(�=��~��;v�����D4��Ʌ���Q����\_ln:B�9"z@UU����0x��Q�\���h,\�����F�-�[�f�-��w�R���5�w%´yv���?�C�_��P(�T�2��s&jLl�'�������e]���_����i����(�,v���kƫg�]=/��Mvv����/��)��Gy))�&H�2��b�v���7oop���f$�۱cG3_������Z/������G�n S�G�\�9m�-[6�'?��w��m4�?3�;f��v�n�0|$�����=?}�-�&�L_� �_���E�75�%�Fu����<�i�w�=|�%��h�Ĭ�R0r:�wQ��J����կ~et��O��\ ���O��ԩ��a^6B�9UU��й���ܭ�����z߁����[gewr����p��M��<x������kS�
c����Z�oP<Q�<�P,��?<7m�u͈��5�n5��1���,���.|�e��/ ���R��چHG'f����0�eeeU;��jZ7n��N�a�#"k7�YͿ18�{شL��%%%��|[II ��u���vW������>f`��D4������(��r�����WWW���%��MN-|����[����� ��qIY��T�&%%�*v9^�C�K���Q�soo���ޣ;N��n�5�����3Y�:�1����5�]0=z�"�������0~��5�$"�PB 3�4�08���y�!DǪ��� ᥗ^���V0cZ�JDϴ>����^�z�A����!�ˈ�1_���iF㤥������̵]�-Z#�6��Gf����P,G���������ˌ����KS�;y��Dt^k�p��\v9r,l���� f�蠮-�tI��Ȳ�=MI���'��LNN�*"Z��X����TC�����-#�S잕��=@poyy96mڤ�g���>�k��'"�o���tb���z��BD�J�FD4��V�q��� ��1b��3gΌ`泦%A������L��{v7gJ2f���U��oz~ڸ�Ew�V=T��M���� ���,^�[���w���{���_ �h�n ! ��>���IJt��=v���;��?��������W��b�3��eFn���қ_SSs�S;��C�BUTTt �+׮] ��huV�����= `m�cK�,���%�f�)���ND����t����;y�w�]�222����D���s���V�y�
�۵��̪i��~nڸ'V�NX���F��1)cu����_o�V-�S�A�Ch��0���LHD�4k;�ݨkcvۨ �p�8_�~��O]�e���$vO�:��$d;1w����/�7f�muw�u�~�i#a�OD0� �%<@��O���/qj�.���Bt��V���cݺuz.{��ϙ���(� 77޷o_L�:����0?$�'�9x׍"��@>/LZ���n���m��66;;[�=і���������.[�-��� p���� �`�*|*�������VM_��;{� �'P=3� 8�2��J'�PB���S�ݴ�����G���Wc��ո���w ��,~ݱ#����e��vr�KpZ4����q0r$��?�Fv ''�K"�A�����w1q�D�a�!����,��޽�73�@UU=#&�䍸�R;v����7n܈ t�_�vc��ƌ�1�l}�@��t�/
gD�LD��蟈h } ��/��I�����s�=���;����}ƌ��.++k7� ��E�@�%7�����:>8�Tf� �A �2c�|+C������>�U.<7m��U���n�� +V�O�v��wdW���N �/]
��H�g�Z�@ez/[ajLgB
L����?є��8�&t�����C����~���h:�ۚ�~�a�X�g���`�c�j3s����-�� ������2�@��٘j3jMF���fU&L@zz��}��m�t��"��>4��Z�!4&&��?�.]��^��Lr�\.�?�����/����i����}򡝩u �pf Ի�+�|�����
H�Ys�{��~�\��}@�%�q�ԙ,=&9w67ي���Y]��C����>y�Ԙm�|��V�%6juh6)�щ.�+,�]�ӧϽeee��l�����G}w�y'TU�r�g��Չ�BU����,vedWa��{��%�?��3���jPi��n����kkk� �M ADX�l~��� 3���f�-I24L��#�D�ٳgc�ʕ�>}�����>n��"UVVVIqq�~��x���8��G6ef:��y>��}�� O+.��-o����i�N�3b��M]��� 1���-@�9v�a�C�4l���BIzzz���ZަS ���l�}��F��DD�7� 6���t��{�$띄�������ݫW��s�E�.�c���UD�f�עE��v�^n�Ҽ�DkD�y����_�c�=�����е3���ھ�!B� �F�̹6ee�4f�jX�;�ޭ3fh��\�*���؍ �b7a�DD]vy�'
ӹ�g��hT�D�Әkg͚�-��!++����Q�~�;���W_m4�Qv�2 ��2��rjli]3!DD;J�'� IDAT|�p�u��Ql۶Mϥ/0��S�G�����D̝����:"`^V����hBQQ�ϋ��^���fv|"����v�Z<���� ����?f̘f�K���z}̼��N��Z�E�!���ѥ!��ɬ��=�V��ϳ' ��|���ܹsOC��9ю�Vo7$:�H�2��4 q$h��LD���$8x���D���Lj=�z�6�������7�VG�O���˝p߃Xa�������i:��Nan6z��=D�A�c˖-���^Ӓ
?��k�n��5W]HNN�M7݄u���G���������(`Y�ю��=�t:gvT�&���?�%��nԘyL��?_�;�S��n���#sNc�tb����� EdJ�<[E{'����K��i�J�S��KII��}ͤ�$<��cp�3���LI�F��1��H�+������_�xo����˶1�n�rj����#G�Đ!C��>�v}�h��p`�ԩx��'�q�F<��������f�������|iw��(++�DQ�\_���+!��t+2W�3F���ܬ��P�5��M���3����Cq�!յ_�ą�(E�n��)v322�EY�Ƶ\-F��{��h���n'g#"jy��p8�l�%Ů�t۷o�`ԫ����:] O-�m��x^P����m��|�˳D �Á{����xꩧ��� ���>��999�srr޵;�H7f̘h��ZG_��b 2n���˃H�> .�U��P�|��ol� X8��5��^iH�z��itK(7�j-n�t�����}dۡ΍3������7ߌ)S� ����V���r��1�e#��TU}�����_�s�Y�(�o>|x�������#..No��(����@ �ۍ��T��(JnNNN���}�����y��
�׻���GD�;:�ӻ�/^�a�|W��#�����A���U�����O�����!EQ�h�:$�T�.\ (��N�Gd����m�Lҵߵ�F�"''gM���-o܈>� n��&�:uJoH���(��C��W hS�FEE���FO���f��'h�jJ�f���ӄ]ٹs�`"Z��{�����'f�����,�����{h~~>
@>]�C�ҍ����zʿu��y����A.''繢��W���|�����D�L����$��N���)4����b%|+����P��-HM��񚂹SF߸e[�f��紏J�\�hP]�o 鈉Aʬ<���$̘2Ů�c*H8o;t)���TTTL���{�1�~����u���� D��ClJ��(u�_�����9]{��3=��0@����H�+� ����L/���������(��{�������cK�.�[���ET��`�<���(/���XUUgQQ�������'L��z+�SQQѯ�莦�Ý.pW\.D�@tz��U���|����Z��u���_]��_���Q�?
_�]�:�Rk��pC�!����Ӥ*eV1230����nz���챫w �p���YOD��l}|ԨQ��^Í1��n3�P�(J�7��SG������3��������;��$�aO!23���"K@@�w���Vq��V��ֽ.����u+�"�P�Ef���Yf�s~L�&!��w�d&��~�2O��<'��=�9�s$��T��rÆ ذA��ԧ̼5Fn�dz՟ddd`��zm�CD9湔�������DoO�䎤��|G�^�c>�/SQ�\"z�jU�V(6�]�Cu�ak������qRr<h?>��݈>�E�=�ݘ ��( <%�3�<��i�NvNeV�b<QT��x^����A�qy�����v�^��{�j�ƍ;I����z������(�9s&��-[f��CD����4�dž 
�P~1؊�#�#�8G�~�o�H�:tڳgO�]�H�B�������]�K����̪�=���c��j�\9��z�v�Z�vnp�I>5
� ��/���Ix%W�bf6l@<�:��[XX��4�,9B�>�u#�v�l�UD4�ڴEڤ��~�d�/��ᅤ�ѣ�
��:�]�˵�����}��EKJ�U�{ J�a��1t�)�1"9��ڳ;ޮ��H՘�h'-��$6>��@ �43אc~��0s�LvToT���.���?1�v�)��"z��M�cǎF~��$:,���}��a�ҥz���0��D��;=�\cJ�ziE�o�b>M���hH|�M7a͚5غ�����D����E�$.���XR�����[
!�
!����5M��C:fvHQ%@
3w�afNQGL����^$�ꃟ?xG�|�w����ЗG r��5_�
vG�Z�`�n�$tWl�Ҙ��M�� ���J#��y80��K�"��.�5�����|*�m���g1��r,99�>�(���:#��i^'��̜�7|>��@ P��n�p���fVk&�H�'����o� M�������ۋ���'>oGb���{���Opi��nǜ9sp�W"hLt(��g3��tXI’��}���������M �k���r��D�K���}���=c�0��׺eو(�^�-LȻ�!�l�z����. ��m���)�J�����*�Z�3��O�ׯn���fG�]�0�ʏ�� �+g��D"i��RZZ����u1�R��j2C����7Dd��1�FԺ�իW4=��Ls�1 i�pnn�f�������OD�S�Ķ�C�s�ۄĔwf̰� ���]$�yB�0��|~�]�D@ʘ��v��� �5�M����DtR��_��#G5{7��ʱ���r��8� #&���JC���3hm�: ��$|�����g2�� �رcz�~�����~����_�f�����%�v"��|����\ND5��^z�8p`4�� ���y'i�x�^���AD��T��a�t$��ә�FJ�� vu��&n��cg$o7$�vˀ���K������|*�^�{D�D��|;v4b�����D�\��~�ۺuk�i�;{�%���fӭɡ"���d)**���$~�A �h3�7�л4Ra��X?��3�b���pZQc���,0��Xe���d�/c
�m�F�D��z�w:�}��{���i�l2k#b캒�(Xǒ�M�V���RGKa�5����'�'�p8����XRR�͛����QkoQBk�Ѯ��333��P��'�$��eM� ��?`f.��/�ذaCf>ŊرC�[���y��~��3O`f,Yr��M"ҟ�F�ۉX�����X�v�p�=�Dc���M=�E�0dee����hK�qg�H���B ��1 v�(�7�P[$#uĘx�a:MQ��kf_��o7NB�1GGVVV���Q !�>}���[o5j�,�v.�Q�f��][�I}QUʭ�n�lO��FB��"'���O�ě�����8�}.�I����p������z��M�t���-�L'���=S�L���D4�a�ׯ�>��z.Wo5�lݶ�y��`�j��뵛��mΝ E��$��g���'��D~�Ӿ}�]��j~���nW����3f���цK�f����@�_�y�޽����� 0sUpf0�mr'��\�vO�?EO�~� ��~��G�^�Z���D8�"X�x�N��J����&''�"�U{���oG�Ν�Z)s+z)K$Q�������Z}�ݽ���ͥ;�mݺ�.E�D�"��'Du�J'�"n��P�8������E�)�����_j��w�}Ѽ��LDݣr,F�|�M��Xz<��CD�z�,��/mժ���M-�U�(�(..�o~m�r�Ҧ�U������[�\o;����T�4M��כ�H(~o'���}T}��r��6"�W� �vD���)i����<�`�sRU� t��]���N,\��3GT��h�Z ���S}��K?�OJ�Å���۝�u=���z֓ݎ�"6_�P� �����}�(.���[�0��l"��U�qIII�;w.���j#��)�%����PX`����_�~X�~�^;W��\JD������`��֦:g6nܘ����u ��� �Ć���_~��E����H�J1��m~����`�����c��Om��u�\�p�z@vv6���:<���F��� �+MpQ���ٳg��� �]�c�.�p� �Yu2F�v�(��Kc�u��"�j�z�ɘ�q*���?�7�"��z���Uԋ��a8�z D��c���M=���+>�/���/���-33��v�Ν{�ŧ�� �6�KS �"����>�H�{%���r�~�a檓]��n�?%%%U'՛6m2bB� LAA���}�]����Y`�5�OP��ܯ_?,\�P���r�w��o�ճ$ �u�_l<J<ρ@ p53\}��k���_m����+�h13�+z/%|�j��%I��`�'"��%%N���{��v%9����'5a8�e�^����a��X�엋��@L��AO�.��nD�|�����>�<]� .�~��}���7�rf~�4GM���f��U>:t(���H;zT���*�&���8T�A��퍬j�+�GQ����f�u댘��n#�� ����]ы���o`4N�u�\z�1� /�Po� 7������E{��O�~�Sn�SU?�0.��R�8a���oD���4�QI���t�(---ff7`,�5�(��C 춉�n����=�>zJ [N%�L %m=J�D��ֽ9��z�s��{�]��Y��H;,�'�.�233����I�Y� �ݕw���H�t6%�8EQ�]<�����H�Aaaa?f�x�b<x����(��
|�h:����رc���� aͦu��w�qG�s�θ��;�1�����Z�3
Ũί ���U��U��j�ˏ�$f�<�M�4fgz$��m�١%�,�m���ۇ�rWb�~'N���D���cb?R��n��z�w���\���v�1o�<\u�Uz��� �"�� !���}����P��?a����kzMM%"/3�?ݡ��;+?v�\�ڵ�޾�*�� �$̜ �ũ �1���% ���^c戔ʈ(@D���,���������n��ɓ��Ψ������Hzzz�#Gf2�f�������W_}M����3�L4�Ғ�'�B0%�~H����VUki��&� Cш��;4-��v8��Q g���<=��#��=G>��s�bf?e�x�w������ v��X,��T�ի��<��#Fl�E�G�&�h Q-�=����@X�j�YN5'�hW�����L��.��n*��غu+�?�wy ��LwJ5���m� ��{�����dz�Z��I ���U'����`w( `�~- ���Y����f�Q�|��w�����Y���>c�FՃ*B�_A
%���FHU*JP;�ا1�Oe��n�ԡ#c��Q�j�b3�u~�,��d�)�vI���HP�#v�dCH�����R�r"��.1m�4�?ި�ˉ��3"�!�ڹsg <�T�OG5��ц���W���iČ�Љ3�o�1T�S��#B�_p���n��̬��5�(�R���k׮8��3��:�mS�������VKNN�CE%�O^%"C��L�X�kQ��8XT vC�(�UEׅ{�E�ZO8d�oXDYڼ���l�?��S�xLA�\����I���M(9���13ϫ=~��w�[�nF�>ED ����z �8f9�|��/�#j�����W���c�̰D���� 6t�E���~ۈ��MvIb7n��q���X�l���U��Mnn�"�\}� eV_HD���Q���^ �F������/��n{�^݂ ����@��D��ŕT�8�`���{@�?e�?���D�c�G���0Um�������m{�m ��F��NY"�ڈh{l�i��|�{��Fse�˅y���n�1��nqqD%h1��j446l���N5�t��GS�jFx<���|����mŪq�iNʼn`08�/_��;w�7�.��gFR'eeeH{��7��M���xS��j�^6 m�F�1�:V�5ͫF@���w+�r}��o��z�����h���T$K���o%��/����6DTu�_�o����H�6
��V��Cu����|"���@�BP��b���S��B�3­�$��TU���T�ٳg4j�=��~��~1�j���/��b��׿6��V"f�c�"�URC� 9��Sqqc?b�a������"�MuHb
B�[�?��>�H��0�n5�D�n��PZ��b�`�4C����,����D�r�1�Պ9s���\ɟ�(��� Lk(ݳ���t-$�Od�`�������G��ߣ v#�����G����ĩ��C�B
S��}��6DJ�nP��"��%U*17$���ߍp�m�7���;'��X�l���7�4ý����>BDoU�8q"�8� #�T�QS�k>���d�0C� �n6ś8PXX�pɆ PPP`��bf����$����|��GЙ�"<_�%++�0���ӦM�����5K԰Ap8�P�Gn���q��Q��YnGԘ[���c?���cٖ�0̜�6�G(�\���d7N5�)�φ�]l�\��W#xHW��fE١Cp�Ŷ?yd�k��<ٕ�� x��OncP�?��HOO7d���dž ��,zEy���h.*�XX�_���0s хaÆ��6�tnO���B�����M��G��#��%���{��oklX,���m۶ň#�����ԛFLVV�q"� @�>/3f�0��RIO��;+�P4O�c���l#К�gIꂈ���z�7b�h��`W�H���ݶ� �D����ß-�R�z���n،T���2�5 ����PXq:��7o��L�P�g�F�){3������T<x�Qef \�+���|k�������D^���i��%"[�3 �-/��V�^md�.�sd%�e���݉hʲe����:� �0Um�����>��p�i������p��x��Z/��2"jt���|��i;�o2�aM�Uf��(((�e�N�{Fцu� -k�'��̾p���=�0Udm(�;�j� i��ٯ 5%�zUUe�k3_R=0�=z஻�2dp�޽����w�PUu6��9��{/�� ��9 �{i�jܕ3xQ ��5Z���?}ժU�?o8k��r(���gf�@���P��S#h8p �v�j��-�Ӹ��|����c�[�������g��P�N�`�1�oчF�� v P�3{�;�IaX��p���o��
v��ne�vS _�D����҂��p��@���t���B���2�Ϸ��f��>>y�dL�<9^nEM����PC$-- w�}w4f�XAD��i(��N��F{s��D43z�u׮]��{�z�z+)���>I�d˖-��|����������f�Ok��h�"2��m�1ͫƃf�Z/PC�i���Ѵ��$��wM �9��{C��D��U_|�K �j�0��ëV�x���g�xղ��7L���m?dO�����"C
S�b�'�mCĤ�=�ٯM�5�D����cw��g ͩ=~�]w�{���p�Ey���c�ƍ3�R[I7��-ͭ�L�����nv�M7Ec�"�o3�x뭷����;����u�_�Y��H0��������n����(>�|�^�>6y�d#�„fz������I/��f͊��=�<�����������w�b�O���5٥fAAA����[s�|d��GD�Q�1����!���E*2�ɛ����(�F
S��+�ټ��C��KW���.���ZJL���<DD�Ws8��w���3���}�w���pj�D��IF��͙��iX�� ����&�5�\s�o~�7m��ʡ�����f�����X�x�޵�7��������1n�8#�.'���xո��|�����p80g�X,�hL�"�����*;r�O۞
Z�q#ˏ%ٓߩ^�$���Q�~�����m}���J���M IDAT� �P�[_�k=$�����0'���h9rl��9�����4{?xj � �go��W��^R�*���1@��Ѧ�����4j�˹�n<���h�>ju��~ ��GD��5��B��j=���o������CDQ)��M�.]}�W>r�H4f�ef}�&%1'� �w�A(�w~-�0Um�^�w��>f�&� �\j��l����O�>����|"J��H,��g����'��0f��…�}v����Y0e�k��A?�>�WG���Ό!��7������/?�!� ��&z���_� D�:z� #R�j\��^�K��寍�M���_�,=Q��(h1�P jP�e��(dee�pi��݉'b���؎!V��j5NW�����3�D�� �Oyo���$���l*�|����<^},-- w�yg��'XKDS�(�/P�@D}Ǣ]�v�BDs���_4�1���*++ÿ��o��g�/c�O�!�A|߾}���i��o�(v5Y LEO�+k����+��x�1��k���Ό��L��Oo�:�t�n5�0�2[���X��i ������<8���x�k�m������������-^x�Y P���T>��M7� yn�4f�Z�f”��sx駀���F8�ql�S��{﬘�� 6�8U���|_0�C��o��v���3.EMNN�~EQ��ħ���'���i�M��AD��x"�}@�ӢE��DT���)S��=V���|GD�Q����H"Z`}YYY�i�{\m�[����^f����@�s�Oi�0����d�t�#�f���z���s�1EQ���G�9�����|t��-^9����?���?Kh�� ��Gg~�jW�3M�Ō��R��|��#}=Ġ��G ���Q�;���zyg���� �1x�(�+
Ϲl��*�� v�d��٣`I�q[I!p��Ob�G�������`#yTl��es�����ђ�c�s��5+0lp<���|�(H߾}��/�M�6fme0��ћDt5����������;-={�,b�k��}��F[�Ԧ����p��괗�:U�,���������B.cf�9}�XBD���0�IBNM ��w������h�R����q/���ciii�={v��&�A�1�c�e�����¡_�~��g�EurBD�ۻ��5��� ���������cݧ����,f����]�i;�����>|eę��9x��y�b<��;��}e��_>����= �
樊�A���.���Dt��n��QɠG�����oV^�-=ꅓ�a���m?�ľ�4f�c���ef0,Сr0==��s���8�f������o���uff&^}�U�z������ܲ%��*LD[�W{l�����E �6�C��onn�e۶m8z4��L���|���� ��KJJ³�>�k���������z�?8Z��rE�c53�����8�ЊG��Vۃ��2f�����c�5Rw"U��FA���{�O���.Z�j�nݪ����ܤ[��������@�7D�����o�55���f�Lw����U���?S��:f�+V���lԴ�vDf.2��J�7�`]����D��) ��
p�0���ћ�Q��JU�.��hQY���FӴ$W2󕮁��Ϋ�e�4Sx
(/�x��A?���;�h�v(��Al�G�O��iTw��;6�$X�� �%�~<�2��)��a�b��·߮ѐ7�`WU����P������G�؋�^b��XR��%���g�,���nR��/=�zҘ�H� DNN�����K�KQq1�߀��{�8zg���NJ���)o�v���/���×_~�� ���j�����J�"|"T���F8E:��G���5 a�X.�B�T��V�M_w�uFRDOG
��p���+A�oJ�~����},HII��ȑ#o�r��a��慠GS��S
��o����o��䄩jӯ_���@`!3Wջ]p�F�] |��,�]�x<����t��� ���ػw節�Gw���/����l��]*���*��X�eՄ��˿X�`�>��f=!�w0$|c�� %����A�>� b:� '8A� 0N�P�������ZW��`N`[R�rȻb銓N��
vu]�+
���c���߶Nu���(�����,��#�є���i���ڳ�t�J���D�B���%���x����F��n� �ׯ��͛��ZT�|���~aa�*�N'{�1���{x�'p�Dl�Q`e7Ԇ M���w���_��0sU�����_|�f�®]1-�jQ�� edX,��|sV�Z�d�}�������|���۱b�
���2ssQ�|@U�����`�Z�_�T"Jg��f:ט�z�������Gg�̙3��կ�fb�%D�3�l�� ��Zf_�������p+������g�P׏=�m5�ECHL�b��u~>�B���U�N;?)6��2���\D^���2�}� 5�=q�GE-(sJR(�r{'L��I�x��?ѧ���v;�͛��/���}>�mDt��4D���?o��6�:�8��4�x<�}�� ̟?>�/^5,���|� 7ܿjժ�����>|�rm �R6�S�J�^�b5N~
U���m������������WG�_���҆����6�L�ymN������OW|�R�o@T�K�2zN�r�ꅧ�U�[q!q�B�D��TW
S�K��18��Z�qj�P[�&������_*1�&�ˉ�Ʊ~׮]m�n%^��EQ��I9�iiixꩧ����]�F�i ���{|R�����g�y�_~9��;�^��<��짟~zN��%�o.**‚ �.������D�|���#G�u�֧�:�%��$�;���������_=����wrx�� ����B��̼��%}I�+�cq��͈s���׿��>��8;u�:L�z��/�v�V/f�CG�k7"U֖��:t��vkS����2�C1nC-��6՞������׍��3_�Z���ƍ3z?a�x<K�y-���yyy���p�]wɠ�$|>���i���b��7ߌ���7�6Wu�v�1{�l������7����Grz
����{�T��W���5v\.�?��*��b�`ڴiFL���@����z�V�*�ł9s��ሪ�Z.��?%u����77��yw�ۗ��*ڏ��θ�����~����7#؍�"s����I+5����|��Ʊ-��Vb�C�vF&3
��bf�������j��z���̌�G����~�z�#��wDt���m6.��B|�����;ѡC���Ht����HD#,�U���,���ĭ��j�(!�X,�:u*�~��_f̘1~���׿Jo��B��~�m#˛lo�S���y@�o�ԩS�fh�>V������k׮��ۢ�{3M��H,��l��W\�����a�/��n��7�o��hlj�� r����D�����4f��@�1L�YR�*v�<eS`-��0͞�����(2؍3>�o�w�l6�Ν ��tm���z�!�~DT�L��fÌ3��G��äI���Ӟ���� ����}KQ��N��b�Xp饗��?�m��ff�c�X0}�t�������
;u�t���iڟ��~&��K�.�����.������[ ըSNKKðaÌ��`�)N5b|>� 3V��bڴi1"�k��D�1Z#f�h��N�D�?<t��o�%>��x�EA����l��$6!9�Θ5���f[Z�����_F�<�`W����n�s΅#ѫ��\
SŊ˖�i��t{��p�?1��؅LcN��v_�p��*:w���;�зQ��xvx�މD4����JD0`z�!,^���?��h�F��ٳg �-3Oc������Y��
�_h�?�p8p�%�`������Q�F���GF�����ߎO>���sOY�N��c�����o�$s33�l7��dz���U�����=j��|���('���w�}�f���Z<O�U� -���f?��C�X�N���I�zwRUt�q�>�8θ�t�~1R=aky�Ne�kJ*ڏ���{� �����5�)f���|�׿����#��U�xz��f�T��
!px��ϓF�� �3�L��!8�|iԶ��1+�"�����Ci�� �^�ƌ��.�o�������z� `t~~�p!ăF�5��raʔ)�2e
B�6l؀իWc͚5���oi��X�t:ѯ_?��� ''>���D����>zR�vCSq�q[AA��B�ǙyH�9�#G��ȑ#q��Q|��X�z5����{�F��jE���1h� �1=z�ѧ�|����u!�/7nl����BlذA��}�uk�� �Ҹ�Ν;i)6��r�9a�7��@ 0����sSSS���⦛��>�l���H ����pw���tFo@Q@DG��A���,ji�$�(pv�g�.�����/Gpb�V��ޅ��{Q�o�k'ݫM\]�"�O6����� "?���z��*�� v���V �r=� 
[��ס����?Qb�����7�F(�x���Q�r98��=U�@��b�'� Bnn�ׁ@�nf�!�s�-�����}�t2�<�r�� �W� @���Z,���"777�p�� �mۆ-[�`˖-ؼy3�lقb�HDh۶-:u�Ν;�o߾���A�=�jM���( ug"77�C����0���\gdJJ
�N���S����y�f���W}�n�jz�dUUѵkW���={�D�~����[;�}�?z��զn.iJJJ��z�uCI/1s�c���񯒒��� �_��O��'�4�N�W��^cE�l����� ���tଳ���_ ���*y���`�:{�F
�*HU�8��&����K�d�۴��}���`o�T�=�0=����| �VKD%ʙ�����ԖH��!%���X�P~� ���E龽(ۿ�A;^�`�Qh%'IS��*l����> ή���W� (5ߣ6�^��������3��?N�4�h��S���#���z�� ܽ��5����D��U1�� ���?5��*�����۔����(~�JR ���ֈ���I�&��y6��_�p�%JKK�=�۱c�9W^ye��i 3����x@�@�#fΫ> QVV;�%K�dΙ3���Z13'�]�S` ��J����
�p����ǏGYY��˫eee�4 �N����ʏSSSѩS't�� :t��z�2"ZGD�
!�՛�
�j�v5� Qq ̌}��a�޽5��G�=��+���f��HJJB�6m��7lu�o��������M���@ p 3�4M3�6ܨB,5j���̕� �߿ @���bDz-�m۶QW_}u��F�3�������皦�$� �'N�5q����^��Ϧ�=�T�%%%���
�֎3fL�sf���;^���_,���[/��uS�ȑ#7N�6����rf�( ������C�X�L�����V�B�W+2z��۷;:4���
�,fn�]ִp�{�Bǎ!Xt�����r�P �����`�i� V H��
��ŝKR ��$XZ���M;XS[��� �2f����/��=Q��`��#"��O��MXwn�j?w�,���lDB��A|w���^�1��n�H����߰�@ee�����yZ;uA�̈� ����7�� Tv#�b��ٿ�51t)!ظqc����|]��U�����9n5$��?XQ���<���� ��CD+|�?^�wK�2®]���*���p�]� ���REQ��4m���k4'zՃ��}��z���+�������0ϻ�Ϻu���B!�9���|�3辠��F�j��7X�>�&��̗]ODŪ�z�j�v#���EY�(�{����md�x�nݺ^�� p�ׁ����j|8JD���ݾ0++밙�ͺ�� �f�T��<��I& ���;��=�,�c���l�nXW��:�S� )N��dee1�r�UFKLB-�����-[����0s6��f�B����6XED_������ �o��ܹsI�Ν��%x�����&� f�U�5 ��|e�Z�������$0B�Yz�0s�k3T����6��T%��������>��cM���������F�3�;
��q���YYY�e�U��B���&�[&}OD���<-76�_9VXX�NӴLf� ���̝��7�+����&�5D����|��@�����D�z��}� �����T α_�����-F�1��Tb��8��(7QR���nw§�����]������K2)))����gϞE>�xT���� UU�0s!DG�=�D�`fG���� "*�����=�2�6EQ�����f߹M`�����W���o(������Z2����JD��WKn�T���#�}�|�VUucJJ�w���M&ח��SU��x�/4M��uǂ:z��G���c�믑b�Z�*//�w�4�3OWU5��o"J����ٳgQaa�9B���ꔔ�$#\~� f^���(f.B�)�Rj��gee@J�M�������-�D~����bi�iZ{fN#���ܞ��#�����T��jE�[9�r"*� �?��2�n?Z��-�Z���s�α/
��E��6�p�§��w�̵}W�҈z�I��H< �i�<q-Q�1k�iZ�b,���s��U�Z$D8��jN���;�K�nhss%�� �ڪ�¿�&��WU�b�b�����Q���(���nߒΝ goDsE�s�ĉ���D"�H$�D"����'�!��Kc�e�2��bYT6$�˶�g�����P��Ǫ������Ʋa,�2�Y"�H$�D"i,4x��&��J$�Ep�Zk��/t�1�`W"�H$�D"�#�F�9� {-ʓ]�$.A��c2ؕH$�D"�H�)�������b;B�S"1%�E�㧄��&N��/�.I$�D"�H$��`J�;z���D��c����Iܰu?�3"o��;�ȥ�%�D"�H$�ƎY5� `'�3"�\<~�[��s�P�1���\�i�1�nz��f�D"�H$�$Θ�Hx" v��g&ʻ���p-�k��6h�%�䔨�)p�=
�~� �'"�J$�D"�H$q��`W����A(��گa+\ ҚU�gIA6;\���9�,�%�? �J$�D"�H$q&��nlw���sP��s��m��D�$�zP8r�p�=
���4�J$�D"�H$qƴ`WO��S!�SP|�T�� ׊����� �$�Sb��I��Bm��L�?�iL"�H$�D"��Ǵ`WU��I)�Z�86�2X�Εˠ�r��I%��4�\X;�D\��J$�D"�H�<D�v��ݙ�^}< )�r���l 3�x�gZ�k�Z� !���,���u�[aε+@%'�2-i���-��SY٦�&�����I�&m7ݸD"�H$I%##ñ}���#I�X,NfV�H�B�R"*QU��ĉ���W�$ CD*�4��6�j��4m$3�@E�M�pN!�a!�3~����Θ��E�0�c�<�4�PyYX�j�Z�2�|��`�8��<T� �;�|6\�j�m� ���C�G�>d�q�D"�H$�&�III�5M���iZ7"�HD�D��`f iJ������� ���{�yg0� �a�6,D�VU���'�>:+��\}��i���=�If�9.���`���?�x�b.3g�m[9vΕ_¶i�٦=2�=RU8r�=t��4�>���|W^^�fӍK$�D"�4!�ș���#
y4M󪪚��]t�(P @��� @Q�_�h�]�Ph���5M[ p�c�Hj@D�-�@fn����W��������mf�f�:ECL��
ԅ ^ �Af�`�qˁ�p~���R �����; I#�@Iii�m"Z�(�m&LXa�q�D"�H$�&Y����j�v!��n23��4- ����6�~":@�2�� wH IDAT���3s���g*�� �JD�U��/��V�  ���6����b�\ �S��B,B��۷X��ŋ�C���B���3c*֭��Z�%�#2{T�a����=j�;���6"�{ҤIo�¸D"�H$IS�U�V]KJJf�4���3� �B�|�i���D8�e��i�� @��b�p��(��FD�A/�1�^!�f�{(Z�̥ ��6ON�~ ��o�b�V�t������d�kNK0 b��|��|��X5�`Wm�
I#��֫�鶉��G222����*7}�D"�H$�&��� �� �9:03k����_�4�+��Dz
KDV�UU���z-�I�:W�D(z]q3˖. @"���1���ѣ��a���-//�?�Ͳͤ�4ۇ���p�]G�Z $E�� ���Y����l��D���by��sϕ/��D"�H$ТE�s��V�$ !ʙ��P(�<���zS��9`/}�i�6�պ�5DTU�&�X/�X�ؼ�F�Xi����,Z�h����m[9^��_¶�[�M'4��dWU�� �Y�Av{��uBD��kҤI[M7.�H$�D�DINN �0 �S�4�=M����ef�CD�l6�-~ IӴ�4M� `933cI�T��Vk9��Nvk3q����\�p�E��'f�I�(7��3�Z��]�(l&���C҈1P�SL�MD_��z����W�n\"�H$�� ӢE�,M�n&����Y���_M���
0���MӖh����,Ou�D��[�[O�4�͍7��}���1�=���,�Z��86�RX�m���/�J�F��K:�F��%��鶉�Gf�=iҤwM7.�H$�D��!�T��y�B�@���_�79Ѕ�n��̩����i�?*�h�>���w� ���*����˗_~�����{��̦�3�@�[�7���+� y���ai���c`?��鶉�0����Ҟ��|A�7�H$�D"i8�)�3sK���G�1��-++�`n����,ED%5H�`���#G���E�= ��23ճ,2e��(����*8
�H�F��r�5t��}��OU4&��vϩ�ݓH$�D"���tf����!�6�*��H�'}o��}+{�JNEB��L�8q��>��ӿ����+7�%�G�,���_���[@�}$d��9�L�� ��+>ED��o��:{����M5.�H$�D� a�ID�MD�j�gVh��C,O\�t�� �f��bfWx�J��ˏ8dv*vŞN���@:ud�$LDǙy7��eee;���5u�Z��l6[!����
!��������u�j 7B,X0���1s?�m[�㫥���n���I�1����Jj��_f�=//o���%�D"�H�!D��t:_`�q��i� !�,M��d�8�xZ��b�Z�Z�֡|B��D� @E��Zc�#����4- �X 70sTi������� Ћ����Q�OBN���7*�����d 3�f߶m۶(**�f�X҅iD�Z�VB�iZ�*������W]K���������1W�{�p3�'���"
2s)�m��� �B���n��ncv+P-Zt��!��Y����/�:`������n���qP۶7�6mb��yyy�n\"�H$���v���Q&3��d�Gf�.
}mPK�n�@"�!�����y?�0s���X�*�� 3�a�%�������'E����Z9��K��B���E�^�QӴ��Ŏ�)�"��Xs�!��j������s��W�����E�`f79X�n��z�z���T�3=VQ��$3o��|4�.�����W�
�R�h'��D�0s"�(�ZQ�xXӴE�[II����'d�)�&N��w����޽{oc�;�v�L�`� �7��c��H�Cm�I#��ֽg,�$�?���='ŧ$�D"�H̅���p�IDmj}j��(5�%"��庌��B�T5�i�"!���l*///���R��j�^�,!D'"�HD����`0�8}iz��n�v:�w�H!DMӖ1�[�`����������;]�UU[Q EQ:��~v��Y"z��K"�ZS�N�DT\3s>��iZyE�ef�b>@q���#�5V�K!�����>�_I��ff�@W!�_Ӵ'��� �X,m�#�V� Dԅ�:Q;f�h��S�����)��|�^�x������u�l�ס((��Ay�pT�Xe|+��$��{�\���S�D���f�Ә1c��j\"�H$��d��I��xz.3/��?:�3s�:�B��4-!�Q�����3�k��C0|R��Ϛ��2����� �X�&"���\���0TUմ�@���O���SU�Qgi��
�Bok��8���J�&��l�X,{4M����������n�ۈ�����:��n.��>�i�P��5M�@ ��jm
�f�����V�ef�bS(z@)�}�O{"Xy�_D�r��d���. ~
�������r"���P(����Q�+������e�ON�O� v+7n�~���O�4m3�g�m��Pr�p�����j9l߯�"V&BV+\���9h�j���a�7�v�ƌ��%�D"�Hb�R�hl�`��N����Ԩ�D�Vf�@f�9
��b�õO +��""Z��͖���"�U�n� �}~����p�TU�O�� gf��i_i��g�Nu���A;���՚&���Z=o��l�]��I6�����|���~� 5M{���j�v��b� !�R�U��RU�Rf^�iڂHN�# t��p�Rq��RӴB������G���-R���ϊ��Aug"��d�:��T Ə�äI��Z,�aD��L��n��ѓPt�5vɨ�䴐���ߋV����#Lt,00//���w�ar�eڿ��,U�n��l�B�( @�$�tWcf��EY�D�O�***"������� :#~.���vd�} !d�t�t�����9����Z�,�<���+Ugy���)��>�������(��(۟��t������3�����?SD>Lr��H�T��Z���,��z^D�����K'�� X`� _SH<�[h�9>�mq�@�L�DdeE����由"������ pɲ�]��uH����EDވ���$� 1��{:��9c:���q�&�ϟ`��$�+q�
�C�}"�E�����ι͢��T���:�ߋ�2s��]V,�6�|���k��������a���e�=�p�|��Q?����Z��伦���b�ZWEQEQ:��!���0 �֞Gr1ι7�sD�R�� ����5�R��vC��0 O���B���)�ɧ,ЗVBϊ� ��c���Š1��ș"2(�t�9���U�R�Z�ܚ�1�APDbd�#]kb�d���JM��;皻+��P�T��˥��m��q]��ۈ�2����=jԨw�����;-���ƙ p�l�%�ޣ0�gb�i �߻��W�������b���ZWEQEQ��Z;@�Xš��z]���!���&�x"�O"����DKD��8�o/p�kΧk��I���ǭwΝ-"������սؙqm�7x){���"2��l�5������א����}$�Ŗ�.�"� ���vE�P8��̦W<��9<(��*��윳��g��7Xk? ��n+�v�nw���߽��n(�J���E"��7@�t�a�:��W ��}`��-G�阽�ȿ��@7Ow����� �rڴij\QEQE�$Iҳr�pTdw&�\n�c�)����HL��� $���D�IN�<�Xk�清B�0G"ة�|@����Jrijδ9�Jr��yӬ���H��r3��zι^e�E�- �f�s���1�DrC_Dz'��1u�MklezE2,
GYk�f�#"y���"r�s�$"y5�^ĝ�[��2� ��[o���R��5�g�HM���ڎ:������{�1��r�v8 s0u
G ��7�"� ��544���\QEQeW�d�/�4�����-"����xo���2���?�M7��[}��1fmږ�"2!��!y�1f2��"�6��"�������H�9I�� ���=��+=�|\k5� "����<�;SD�L"�7�g��-S�1�A�'�|�@���7���r�\+Ҍ}:� ���*�K��r�c�x�ͱd��V(f�ax��L0�Z����m�}8���ޔ>��b���ٳ_�����o�ZDN�UlWW�M'6����PXv��_�U���1ȿ�}�?~&X�.�_��󙆆�G�GpEQEQ�] ���KG'���$� ��+~����u �E=������}���v:ɬ�����~_ʄ���)�$��q��TYk�<o5��|��4�✫��$��ne9w�KI�qג���[��O�6��(d�ƘqH�C�Fh����0[�M2�2�䄊�p�d��$�ss�����"����)k��s�x��־d���[����Q'777��o�ȡ��m�G���#x�����U�
��p�g�ohe/�m���|�X,.�ypEQEQ�]������!�sz�D�p ����E��$�7��݌sZ��o�����-a�����
�if�A[��ݑ��s�ܾȔ���Ľ�vv�k^0K�G�O�����1�Sa�4}t0�𺈼�d=�湧} Z�8n3�Tf��p�U�Lj�:��c$S�z��;�s�=/���=�GD�X�@��[�k�^���V[�G��2���KܺhѢ�|%-�� Ѹ �� ��C��;a6VU��/F�E݌���=¿i������ǨnM��(��(J�,t `����{������p&���y��pn��Tt!v���l�E�smA"Hڑ�B�}w��5�$=����H܋=T��1fokmU&Z"�N>�I�^�Y�G�Lɂt4��1�Rd�H[.�{��"�-���j����7�(Z�䚙�[���R�t?z�m-���Y�G���m��v�%�f��GQ������8�7"�}��v�)vS\�X�~���7�Y��Ӓ��ګ&�I���=h?�]�?���W�QU#vI�A�1����\��x�M�]__Ռ3v�'��(��(��"rUv��lp I �t�$#"���-bw� d��@R�
$mm�r�%��<�C�Y������Z�L2���f�����(Ƙ�����c��w�q�$�g�����68 �ȫȸ�N�%$�f�zԨ\:���t�.��z�u�J�����ظq� �&��fɃ���|�Y'"Cʮ�"�Eѯ�Tl ���!�L�1f/����8nF�=�{��s�1m��x��묵���x�dd� �s�qh?�pV܅ܓ��&V&�Ga�qp�рWs'y�g�\��'�|���(��(�ҏx����� "1 �$��t��H�k�ER.{��j%��_U;Q� �#�@�>�$�cfx��j⓬��rDžax6�i"2�/"�#��cY<��sι��8^�D`E$��ͭ�R:�H�+{Q���ܶr&N��-�Ki���o ��k2��` ɵՈ;�P�v��s�-��#�� �o��9o�0 ?Q*���n����Gا\�l����k��z���)"پ�4�4�q��m��)�]��jihhx����B�L�[纾"�ifZ柍h��Z��1�”�r�'1`괚 ]��}�𦦦���UEQE�v���d��Q���'�N�<?-Ζ0W-v ��D�Ii��������e��0�Z{����R���(��E��J�ҥ���t�ݗ qCD�q�'�{D��
�g�d�7Cr��y�F� ��T����HD���<���a�C<�{�O<n�c�9�8��nʽ"rCz���� �`�N���p��>M��(��9�oHւ����{�@�N��Cr���� *v+hjjz�X,~��4[Y�o v�p��r:6��g�#kz��;h2��}�gͅ���2�nj1 �bq�ܹs�Z����(��(������3���ؗ��:���H_����8�J֋�bov"t&x��@�9��q�k�eι˜s_w�} ����>'"q��-��w<�L����>��3Ƙ騲ERZ�].�-o[�LִT*���
N��1�U�l�`p%"�ι;ѱ����U"�Qٙ�� c���0�<�����I(
3s��O�tι8�*"�x�V��ι�-ϥ�1f�0 ��洪��˘��X,� �؛o��TW8�V��q����"��㉉U�Z����q����q=�GH�A򋍍��c,�EQEQvSz������bWD� ��Ñ�ƙ��Oi�l �/��df<!�8 �yF�H�q�D�� &�_"�'G�ԅ�K�|>�:�a��ۼ�Z{��y�""^���$cI��-�ֽ�ۘ4K���u$e������"�ue>�D�O�e\� �p?d��ι<�I��[Q}����1g��*�M��\.w�1�ɺ�� �Ba��Ed�s�FQ�-�X��ϥ;���c�$92���:7 ÛJ��õ ��n�455�v��ѓI^`u��h?�P�?�|�}�jz[���y�a����\��h��R�R�e���(��([���l�pg�M�"v��M��ҷ1�Z��|�Z�K$=Z;d�H&y��y�IVm�G*�j���~�����}��1�K2o�� L�� "�LVt�����H�Wy\c̈0 ����xWىYDڭ�?G':Eތ��[�Ri~��p���{�99�8�f���"r<�vF����]D^���My�9wSf���|��Z�b�L�2%*��x�7�!@[͂�>�9b֝y>J��sK�/���9r��4���-���?����˳g��X�EQEQv#��|H����;��klI,�C&y�I�""oU7Ŏ����.�?���t���$'z������m��7��Q��D�/Ȕ���1E�e�1��������"�k��NֶZk眻�<n����Y��]�{CŴ�/ �����5�-�s�]��(���&�I?훜s�F����ߓ�;�Ve��Qմ|>�}�����
�}����������$�3�03)��8}Z>t��jV1�3��G���]��G��7�ZB򰦦�s�̙�FM�+��(������n���Į������$�t���U�\�\1fkE׈�R�ɡ�9��H^�y^�ښ��Q}I_����g��|�ɜ1�<�D�� "�=�u�X,"��8���<S7�\FApgfV��ɸH~"]+�(���=ժN���"�^D^C��{�W f������f2-�ʥ�ݭ�}�9we����`l^�?���� ��:ۧb�
^-�gx�[k� �֦���� `�U��[�;��z΅��q2L��%$�B��b��P,�� EQEQ%ef/�iȼ~��c�b��4��O�� �Ү�����W܅L�n*f|c�{=ϻ:�˝Grxw�jMZ�{G�T��9�5��| ��=eI�a~�d�����s�ERޝ��Q],"�b��1fZ.������$'�a�]��(��K�����.�%.ke��A�9��F��1�t�H�^u5�u���Z�����<ɓ�1���r��<���7ICrb����HB�F�Z{4�/������� Do �z�w�l�y-��ᣱ��z<.7>1�]�)^א|��k��VEQEٝ!y-� ҷk."/wq�<��} ����XIL����!������kw������1��Y��'��=�-�R�G�� �;���u"r�k8�Z�8.���Lk�U>��y��&d�T+�ߗJ��x��\��r��,p΍�֮t�] �z$N��
��|{���Wc�Ö �;ƘG��?��h ���X$GA0��$u��q�*"��t�/�'H� ��1f&:fsK"�s� q�)�� &�#y�1fj����s�~$%ћ�|Fm�sGc��<���}�|!��ټ �IDAT�~1�<-"+� �:瞍�x!ѿ��b�v�E�� �"�O�G���G@��{�R�ft=�]o�p�M? �I�rzeZ�1W�9��S�L���ኢ(��(J�
� $e�'�n���X`j��"��n��`~���� �E�Z̽�q}� ��?W�����s�"��8�@�Gm/��������9�)��$�f����y�$H֥sjCR�\D��@c�{D�c�s���n�����0��8c�G<��ɉH���Ƙ�܋Ƙ���:`�1f��v/y"�����jd�}���S�#���w�^s�]E��]<L�1��%lm�%ZH��:疋��"�jE�!�1�\�y�>s(�)!�!\ݞs����"r%��vy�U�֖�˗�~��I��� �elӶ ��!|���j���. P7m:
���W�[�?���Ϛ5��ZWEQE���%$��^�C�6h#�c�_�j—("���E7Wl~��hՒ> �<���9�d���F�UD�p��<*"�EQ�
�u[$�p@{��Dr��LC��6�������x���d�� l�1� �1Ƙ�Ƙ<���tl#"��v5��s�,�F5�մ<x/�1�D�x���1CSN%��s��s�;�X�E��ܻ4�<���}�1�H�-"c,@���̱@�]]眻�T*]%"�*����nt�F�H ������5�,��I��<o�V5i��"򐈔��X��=V��}X�t���7~��"RU��0k�ƀ�K<�L��b�~��S1������ڈ�"�-�O�<��(��(�D������攧E�3'�l\I�rDf��E�k�0�>�
�qƘ�Ƙp�1f 2�*=.B"��DιV$B�"q=.�B4t��E��5"�<�{��H� �Y�n�G�Y�0 �A�PcL.��Z$fN/ y��
��m}X�> (�#�?#��u��d�[��z+}�����$s�����,9
����=������.�N7�ED~P*��҉�C�H� �����i�C����{P�k�����!ɸ�xT�ngn���8�RDN�����
,�ު��8+vs��uǟo`��d���$?S,�w{WEQE�Ө�����+$��Ed�67]ľ���[ `|�ʻ�H����S=�s�1f��L"9��N��I¶]DZI�8�Vx�Z���,�א�r�kTe�� n�Y��C��*6�8v���q���r���>*"NJH���skE�q��ߜs/"qr^�����1�k�M�r�snaE?��C�j�܃�}�~1�{ۥh�*���Ò%K���^��[Ԕ�'PX�kruh���'�9����+Ƙ�����EQEQ�m$�v58IY�H�|{%r3q��}��O"���N�os!!SΦG�Fi�!Ƙ�K�}ې��HL�� ��H�O;��/�gO��\.w��y�sC�s/�q�k��$6"� c$BR�ܗ�A\J�u���R��("O����&T��`-Z�W�ȁ��Kk��z5��a��d��|����;3f̨��MQEQE���Gӷ "�dg�'KZ��!�$��Ҭ.!Vά��Y�] 9�P(\K�QD��8^��H�l衜�0��� H^L2�A�s�DQ���}�B��N`�ҥ��M�>."��Ȉ���9��\g��RCC�[;{>��(��(J�H�ƾ�dm�sH �T손4�B�"rɽD��'�(:��})?&9���sH~95�*��T*��_�C~χ(�fƌ1���s�=?_�n�B�d���
����y��;w��;{.��(��(J�|
[z�~�����Ap�����E� Hz��i������.�����9m�")!�����.��7�<�>�-�;�c>���p�Μ��(��(��wH�m$Y�C���%������8=e;����<�" Eι�Q�A���o��A��܇WD���jZ.� T��B477*"���;zl�/������F����(��(J���p$�j��"r�N��� �Ap��y��VB���Ri:�g��蓬�p�'�9�4��Y�e��N�"*ill|�X,6�<�#;bL��,4�����_P��(��(�ҟ):���ߐ��U��{㑬�ͽ�D�I�Zv}Z�Xl�>�9�H�����%)������|�s����i�����/Ϛ5��Z�WEQEQv<"�&-e����U٥�Dd)��%Y� Q=��q5��7��n�snqm��c��466����� � lM ��D�b�x�
]EQEQ�� i��T��9���{����4&y>�I$��}�\�4C�9���h�;]��O���ۇ����9� $h}��
��>3w��e5���(��(��(;��a�g㑖.���C"rQ�+�R���~��y��[��G<՟�U��3/^<�9�u9����y�% ��nSEQEQe�A�x��ac�5�޸$��"rC�T��ߺ�$LJax��\@r�s��8����"�K�*v�)���SE�j9��cH�� &\3y�d-cQEQEQ����1�c�� �z۲9��,9���+�GE�E���P���^ǐ霃��&��U��oBP���inn��~�l.������3gΚ�57EQEQEQv,$C����Oc�\��"qY��҉���x"��9w�����XןJ����� X�t��q��sE�r�wc.ihh��Ξ��(��(��(;��<�Í1��1G��_D��,��CK"�Fr�s�yy�9� ��Z�c67���� �EQEQEQ�pR��#1�`��$��w���G��BS��UEQEQEٍa��n��b�fA�_˕�����A-��-IEND�B`�
�PNG

IHDR���ӳ{�PLTE���������������mmm��������������������������������������������������������������ⰰ���������������������������������������������ᒒ�������������ttt��������󻻻������������bbb������������������������������������������������������������������eeeggg��𶶶���������������������������������xxx��������������������������������󛛛�����������������������������������Ƽ�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������몪����������������֢�����������UUU��������������������������������������������������������������������������鿿�����������������rO��tRNS���#�_
/�����oS��?��C� kD���OS_������6��>4!~a �@1�_'o�n�ҋ���M���3�BQj��p&%!l��"Xqr;�� A[�<`�am}4�3/0I��PCM!6(*gK&YQ�GDP,�`�{VP�-�x�)h�7�e1]��W��$��1�b�zSܕcO��]����U;Zi<N#� ) 86pV��:h�#�0Z�Q�JN��EDT��~��^-IDATx^읇#Ǚ��b' 4A$Ah� )�p�3�<M�F9Y9X��,�r�i���ھ��|�s��t9�s��޿�X� k��jv�@�l_��I��*~h��>�'y�"�������؆�K 64�Y�*.v�@���c.};��tN%�DI���� !Z�Џ5L�H�2�6 ��ɯ��"� �-b�E,,)�ʏ� B���>m�����n��6pm�R�O
wm@���V�#?�'C�ȑZ#��q����b��|$�:�)��/E�%��nR�q�C�hn��%�i�̓�����}l�m
?i�d�d�"�,���`�H�"r.z�����~���(b�Q�U&��)�5��X#�����EM���R<�*p[�[%.�O�̣��k7�lIo�������J�F� �lV!̡ăuH�`��������&�,�z��Rk$���|$�l���Xb�����jߪ�dU��?Σ$H���W��$U�'���H�E3*խ����U\}���(�
�zhVk}g�u�Rk$��%�|�T�|��ck�獳"��D����_W+����.Q���)�@���ƽ�H����b�s��l��T���D���R�2Xm�#a ��3lY��z�j����㒚#!� 4�J��8�(��c�v���t]�a��T���� ���D ΅��Q?^-��_^$:\���V �$��N|�=(v�Z'q�6�Z�׆���B5V���!y���3��K��㱿b�v4��x����R]al��!��I�o�P�@�t��Vy����L��٪ml�ڿI�Ub|[*��lke'*�Wd���d���D�ӝ}\W��_Wߝ����r�N�?���vޫ�۲X%��0u��oui*��JV��Ʀ�b%�}���i5I�YlN�E-w�ς�f_W3m�I��������-�m����Q)�S ��k��TC7��m�<"��܌�b�T|��'��$�Ҙ�����R&>��O p��������6�������t���S��N\�ׯL��m��\�����r@�3�u�T
b7��t.5.q���3�r0�=�8T����i�J�\��6uF
��R�32^���'Ū�����x��I� ��F�8O{%8��kJ��MS�ȴd�BEd����W��CY��O:/O�N/�I��_=��xFE��! �=��i:o�~��� y�?��'��'��[͓[͓[͓[͓[ͭ��.�U>�$�P�Ʀ�c%�]��\c��:�| �,e�S�Z,�o��Xr����X�!�R����@�Z�v� �0��>?�*� �<���|����N6�0��;{�a�d��2��v+D��^t����[q!�۞V}��f��ۨϏ���Y��eॗ��)Vy�l|" f�U��q��@�Ǽ�4Y-��Y��-!�6a���B:o%�J��I���UQ|�U�K�O�`��=\ ����:�0���x ��Pa��u�@��!�K��P�d�xhw1>�$j΍��v��Zd���x��S�UA�&[UR�d��7�ø��z�k��/���r�U^������w:I.�VǮ���c>q�.!�zS�r&���2�)Wg� ��R -�i�Q 8����Pa\О�U%�iݡ��U�_=��p�� �Lu��(�N�?���0?�Æ:]�ά���t�B%�U|�����NsorN��f�� �,�P !�v"
Y�6�hL�_�@@�b�s�c���qg�v4|��|0lϟ���$S��9����bʱ��j#���~�����?o��}����}7sAPm:IV�=n���
!���{��{��h��Eࢪ�8�s�u��oL���T�$��;V����s��cq�D�3���� ��༂3.D�B������B4�&�V'�� T� `��D�6����Ϸ�q�y�j�8V����*���X%���@s�\�jrN�$�|�=5�Ά '�mU��i��K��i�%C��I�:ssaƅ`*`��=�l��)>�u՘MeuS����I�_�O��L��_�}�o&���jz���p���{�����lu�:O������)�s�%Q@��$�<]f� ��xO%��PCbhr2���������PK���p�f5�Në3^o�����]�e�J����i�B��464��^t���uٲ�U֌:G4'���22Y�p���u�G'/Py�4?���.��SB�P_>����I 1t3Γ�B�ɭ�ɭ�ɭ�ɭ�V��V��V��V��Vs���]�!�67(��g �����y��@�� 4>Q�� ��V�F�}^Xׇ�ڼ���j���e�26 L���%��Y�G�h ���l�C�}�)��<
�!�E����E�P�ZWZ���V+�@†�R 5{@ou�ɐ�4���&����H���6�e�y V��݀�Vť�����cqZ�ޒ�r��J��yB��y���Fz��FN�$��Hb����*+�jՏq�э�� ګ�kݿU�X��l�e������1����d�0d^�-�B%���}� ���{Y���%r�*�j5Ak5�u��"�,�:~�Ҹ�Y��~
h����SA�~��6� ��fu�lՇf��{ȵQtATH�Z�k���ƭ/_���S��n���
�u']b�]|m`�B��ā����J,O$��du]�Zs� �FL�:������a�����Ǚ���T4�o�~by?wp�j滥�A����(�x�]�†����f��~an֧/����^�d�ڲ�c����Շ,!��1��i&�xi_VK@ip�̓9���Vi%a; ��L?�0J�*���Ū5���U����'���x^�6�V[�^ �{�eU���|�:0�=0���d۫o���*J�q%�[���Y�N��.sQ�L�ud�[2��9�I��:W�n��������m�Xl�ڃ�6�!l�Nl��V�էKU���jV�\J%�Uߊ��B��LcKf�b��>a�=�b�~�R]aG%[����js@�<i�[Х*^.d;UI�R+�OD�2e�ܶ� ��Q��N3�4"1�������g�0���u�\��I}����wFV��4y/D��j��j��jn5On5On5On5On5��h�,ҷUr��]��]L^����%J��D���iɭ��G�ԝ ߴ�/�%='q�å)����:��Q�<�X�.��'���[�@�P����v�/ɼ����>/9�MطݘU�>yɲX�@}� � ��F��t�g^��vO\��Ӹwv�p���z3��K5i�!$P>�ā����'��Vƛ���L�2r��@�UM��K��Z�����6���tw�맟¦b�m�1�h|�|�]}~�0��MjA����(J�����JP68�C&yr��׉e}�j�_c�J�?�I0��k��>š�W���� ������|�B�ޝ�."TEXd� � �8��!cw�*E(�J)���!�[W"�j_���ТeX_��XB;���o��O0~?�:P�C� (.��[�����!Wq�%��*le��Y)E�<^�K�Z�T�60�.���#���A\���5;Rm�tkd�/8�)5~����^0� #�Ckg���e��y)����Ͷ��Ժ��6ĥ�<�(?��&���u�A��V���m0^h�.�t�xR*��a��'�:,�H�|�ō��� l5z�;8+e�#b'#|�}2�w(|Kc�J� �l6 �����w��^�Տ�o��i��3H� �R ��̔9�,Y�gP�ְ:N �[5S����R��!���[)��]���i}`���m���N�4Х���v�`|;f�(��F�lt���L�8��÷Z#�AO%��Y)N�U�5Y��e��d�J�E�3dZذ���<�x����ɝ��e �@�Pڧ���F�TR ��2S�·��Φ/u�Z�~�C�3���X�z���U���x�\2�s���e �D��D.���fBO&en�'i��R%��?Fy�VsS~$u��m��w()��r��o�0*D���i!3�:On[B�!sʇB�p>ݣHT�1�� ;�8M�jnʏ��Ӥ��qp� 1h�^�<��<��<���j��j��j��j��jn�����q�(qp�Ok���}���I?TY8H��mh�yK�̝u5�����I�t�e�nQBޗ`�R��`��E�P� �ڦ����x�����>�>����yt�{?|��'j)�����}YU���U���{�@V�/�J1�F+���7䀉[OW�O[�� ����y���UY������!?B��D%�D��Wj�>-Ai6x�z)���U R�����7 d���@�g����\�so�)�a�4�zf�[�W+���>��� ��P��>�
|��qL��G8�v���ȣ��l�j���2Z��t��+��V��A�6g<�/��Q �H��SrΣ����d}��Y�q��g]�sY]�;]F�C�@5�Y��Ֆ�5�C�3�8o�)k�1'��d6�>T *�ʆ��Uz(�m)��CD `��He/�.�:�zN��9pgo &N�C�׃�އ�>�W��հ_��Hj��)�Xe6F��7p�m�-�`'�c���.����AZ=���^�e8��F�;<����J1{��+8'�ɪ'�և\A�*����[� ��R$U�Y)V� �AyɃ�w)�Ec#<�T����\vW<�U1�IؘCDo��Yo��]�wm�aw��:B� :'�Z+�v�}��|�0��q���1�P�΃�*��u��T��7 �F3��9���A}$���f�+�o����[��I�5��ʰ�޽x(&����i��ʼY���:c�Pp*��b��¸J���j�V7l�jtsNk��v����[�fy3��g]�������u����鲱���g�J��E�0)Vił��ù�����\vW<�Ug�t�e�~B�[����A�����H�J��'�.��n��& 1Ԕ�� ��o%gͱ_��N�
���5�.W���3y/D��d�yr���<��<��<��<��<���j�ܪ{�����waw�:6�dJ�;&���3�p tl���as������W_U���T�_'9{?�a���Ԭ���l/0���dHgqll�c����8�R�y�����m=ˢ�_�ͺ�[Է71�x"�"��S�IfV��r���x3�3y�)h� ���h�ՠ��0���?�r��5�x�����_� -����j������� ���чoO:��$���XBXJ��ѣ�1����#ֈu7�`�zu2��{�\;��uܗ�9@�0��V$2X���S�����&���Ba��[�O�~��j�N2ߠȪ/����jz_���nA��������~�� �u��h@GL�O�eɵ ��?T���f<V�����e��@���*�-}�e��@� �0Zt �/~������Xm0�*���*��H'\������u��S�E��m�Lֻ��6������;+{l��5۽����?u*����_� Ni-:�I@,;�]����W�Y�` *���߀n�SO��~�n���W�P�.��c����Z�T�u���Po^ǃ7����w��B�RB�W�_m�dj���������B���6�:��*��H����]�����d�Q>�{R��������t�n(��z�!S�7o
����Ie���w�3]��bܗ���8�5|�i��Ϡ��R��JkʱZ�RO+�8�U&�:]�Z�ieR����<I��~�|�d���,�j��릟�{��;�7�U�݌�X�B���`�����[�u5~�=z�q굵Ű�޹e��b�c5���o���{;����ߩ�@;���n*T� ĵ2�$ܨ��0�'�Y-?
�j�[�Z��j����ӭ�v���i�-�*rD{�mL-,L�=��y��m��x���c:���We����vұ��oÏń�
��"dF����8[�T}ӵF�-�I��V��lV[P�����)DV C�8ݪ}|kZ�� � ���{����Y�|��xrr��xa���G_���>�(��J�M�ޗ7����Z@��5�a^�\G�z��s���ρU��*�rM�e�zT�^�:ɬ��ͦX=>�$
bi>�U&X�Qoybb�G��k��8� �
�Ҙ���n).Ս����o� ��^M�m�d�Z���i�$s��o�o��*{�4���eLb�Lٳ"�"mx:�`:m�k�[�geT���ެ)���'0*T��B�{!��I� �'��'��'��'��[͓[͓[͓[͓[]�Z���j Q�.e� '/��y�vQ�71�(Z&���X��?(_��Z�����){t�ڀm�Z���W�Ϗ�)��-C���� jq�n�,̋�"�Iv���UL�!h���꛿���s�k��AcrN���佚ф���VE4�0�y�X��~�4zʸV㳰%��,���)f���qt�p�u�~�  ������*���^��0:����ܲ�3�3���J��O�(�����ZB?K�^ �v]�un��l����W����i0�p6��[착�C_5X�#�[��wX3�b��廫�R�{���NK�A�����e S���e�|���w��x���s��o>�P\儔ԕ6�;nV�m�f�I$�����V͓J-�J%֌��0��Uw�YЎ�S����n�u��m�藮���xz��˗V�ƫ�I�vn�W��_�qL�Z����"_�X�z���� 8�]Ap�����?��C�����5�4��3�zw(�{7e�*Ȳ`۰�!A�Q�:�KUn�����z�]�1y�V���Ga��C��m0�PY
ٚUx6TT&�hV�9V�
���Ӭ�zÑ� 1[�X�z�Z�����9�e�r�q�J���ND�/���g��X��*9o���N6�D��` �{���I�%�M�z9—�T�Q�������7f�\"j��_3����~xB�'���ܷ��Y��]*KЌ�%"���5�"��qxq~���ƕ=����j���S�>j�V�&~]2�xz�F����1X��_y��D��<#N����RB��}K����/���i��y����� !V^��˿e�J���}/Fk��A��7��� ��S���+.�(ec���J:�z����W�Z���몖w���Q������~a����̈́�p�6,e5�,�+����,���������t�v�%O^O��O}�ן -O��7>e��kC�6�wa�_��C� ��|���9���*�����W��A�)�U�Jg�8<�Z���x^?���2�u��Y����*^?��ڇKC�Z�[�����0.���C��@m�����$-��/~�|�Y��[e�w�eQ���׶&c��O�4s|��c��J���ws��X�8/��6�/ ڼ;�'F�LN^�8]��ead�Z 1'������^�������L��sBd�%�+M��`��SK��8פ����*��)gl�#�3"��gъ�S �����qtcxx��|H>����=��:�����m�j������U����v�q�y��s�ܒ�Lgl�C6+[F�SWg���9���wV3�1�A � �N��D�<�����$5e�(s������[� ۨb�����aF.��]�K���IEND�B`�
�PNG

IHDR����tEXtSoftwareAdobe ImageReadyq�e<1�IDATx��}ml\E��W��^ɺ�D$|nw'��;vю�8�m0��k<f�8ـ�<�h3$�  b,mn�� � ғ����0��L Y`6s'>�Q������S������n�S�V�;1K�G���s�ԩ�>Uo���TU�1cƖ�Yuּ��c��a&���#C,pؚ��>kں ����U�LW
-s�n�3V�q��~N����o��c���I�~L��{��- ���H8%_��M�£w�B��6EW��,Ģp������Y�2+�(Y���@��&��A�/�����3kX�h�ߍ�-a����A���<>P���'\���J�;(�}�#�Qz������:4��%m?nf�ntK*����l�9J���+�D��I��Yu1Y���Z^��(]YYE��f@���О�lX��z]�U�t� ��u� �&�5-P���W�}��@t�|�#L��Y�=��s��܂���,w#+�R�+?�Ƌa�x�� X�0�"ea)�t�G�*ԡwV�w�V^��rf%xB(qּ�4>��W�G�#��lWU<Ё����XJVѶ��l�����R���$k�DVr�I����7:�X<�s>%X�1��N��Ez��w���;y��9�z�9�O�%~��~��u��ɗ*�=�����I�x�c�y}��Y(���o��u
±N$�^�j�� �e\��iX�񝜬]��;Y-�r����Ѳ�&��>�!�zlY�aVHVN԰�9=��]�=������mR��M���d��OUC �JUiT}r�W��W'�ڹu��)ʢ����F"YU�#��P�׾����&ܑ�Ѕ���R�O����wyz��m$���O����s? +^�FT�����I�E�q�%��&�����~� >�M���}]��Ԗ��w�A��?
[���Nteexn�(�措���B�d��MT��pʥ�nq�q�S�?���bW����XmW6��x*{V_����!V�jΧ�s�VL^j��� Xk�Qj�U����6���sk��̩n~�[�q�Ǹ�-��` �O���:G�����7��l��"k�������sR�e�2��v�Q�=�QƼJ�U�X`�g�Qy~ ď�K��Ȱ��E�]��#��P��:�t���d�\T�/u���������;�س�:�J�c-%'��e�� q���
?j�"/yh���4��8�Zi������1�|JU���u�>��_���N���;hxw�NU �J�QU7\�j�̮bT�:�����B�?6����o�J��1Ί%��I
UY-I���i4{�=�rǤ7��@)H�K�J+�f�4�X�8C�d�?'j��1��� ��N���<� 3�9����E<�w߬��V���z�E}��^_e檴 p��t붾3��9�,��?���g�l�Y�O���<���x�x�|���a؎��Ue����F���� �1�;��{ EF�0`����D�R���+�U�YiD����4�?�Y`|B���s2��yip�I�q�>W�o� �V���T��G��zg#�
%���D0#ܠ3���[t�i�آ�( U�,�]�125��|��N�̭fw�7w� ��� ��u+�Š��]�D�b]��K� ��xbW� ՛7|�В��X㕛���{U����c��G����X�k¬�|�(�h)IU�a)�lp 3��l���uPU�]D��)�/7~4W��t5�J}��V���
X�0���z� �VM���;>�Gԙ�^���|��gF:��jaZ��^)74C#j�wr,еS�l��G�u�;1���v�m><�)�}��<���VZ ue۠D�+j�y����J6V{j ���K��>��Z���QՖ���&�mZ:���1�U�MB�~���
�a���:�/᜗:K�W�WOҠ&�����Y���2f����7cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘��g��*3f�F5�Lb���N��2#Tf=C�`!��ZG�Ue꣇�e��2V���<�1mkS����4iϗ*.�{N�8X�aj~���ڀ�nA�x,���%fE:�|�Y�DV�j
��¢�lg6�(:�k~���M�M��5?�4 ]WO�>��诋W����Z�iG�|�Q�G�Je��K[Ycյ�pmjE\f/�ǎ8&�OQ�3� ����.3t��t2'�-V�8���p�X�S�r�Y#J!���Q�� �"�,ub�@F���K�:�u�^��iy��[]<.Cw�����+W\�)��b���
k�r-���.M�t�ڀ�M��q�ʄ������۰����#$�^X$��"�������V`�T�4�m��~�w%P�p1��|�+&Ux�Y��8��*�r�8:�����k7QЃҀT���������$��Ў��ƙ�
�S>~�S�����j�s�:5�q.w�&_Z.�X=����:ވbw�`��� _�kd�{'��0�:�d��s��#�q���i!224����nq�\�9�-��KUT�sSU��uVo�@;�U��z�>^��=��N��������p��>o��P��O���
��@I���@���'G��j5�o�*U�>��^�*�e�w��>ͫʧ��᫠Q���5 ̈́���<$�#�5�J�ٻ�j��6e�)��_ ����d]��2���B:���^�(*�:8J���Y�S鬆����Kݗ�� ]U4_�rj�{���5�ׇ�aǑ/�y�V��?��G�t��G�����b@xPU��7O3�|�鍪 �I��Q5��Q��Gw� *(;�w�f�0*�P�UU�<Y�Ɣ���v����b��t��5{2!�,}����Ҧ���:)��j2Ok�Ϊ�' ֊0I.q\(�%ojQ���ĖՇ�a<��ԍ��ex�Agt��'�[d;׸�����`r�cd�����j��P�FU�$�UeJ�I6�T���&Z}���z���(�z� vfu�z� ��{}ۿߝ��ݞlx��U�Z�謊�.�Y岟b���%�����nw��@��ǩ��S9��|źs%�>�_�o#���9�\�EU~�/�ځ�t(r�[�Q��Zu��Oo;�����!MrU�]��0T��cpDő�?.���c��Pu���F������;����L_�������S��b}�R/�J_��+��h�2$�a��i� �U�ǩ��S9>��Є}7�6r���zu�����~国4��oĨ
1J����
��^�̘��~��iC޸�5��5<P�ھ�r�/�G��Y�k૵��5�mK
��2姪�Ϊ5,���?�1'�jÓQ���pT뾺���
*��~�I?H�ם�):����\������J��:3�ѴUGo)X��.�Ë���*j�\��?}�㉎�G~A{Y#�W/3��鬶�!ʼ��=��C�g�u *���u_��ޮ+�Qe�5�w�:���U���K��?U�W�1j\��S5/<�z7P^����<,S��j�UU8�����v,�2�_��_��i�뻊��^����R5^v��Nl>G׹]�g���w��s�n zTuO=�?/����zƲc>��Οb�#7ֻcg��k��ޛT�U�j���*-T=]����uu}��>ݨ�NЭ ���[ ]�:%/_� ��S�z]6D.�m������D7Uƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1c����>�J4h�PP��+��A�;'�G_�XK��mL���5I.},wFFu��m$S�-�E�-;Õ
C3I-`�B��R��x1��ғT�Jݕ;hΊ�8� D�Y��J��o;������Yš5�M����K��ɰM��;���%P���d9K�h���n�D[z��gVh�,��'C�
p!^M�(�WK2�X�>UQ��%���^��p8 ˽�^#�Ζ؄+.@���g�C�z%ɔ-Pr
���K�X��
����n���>���=�Ք�Ѩ��eSvR����L�z���5%9UQS ��\�W�ի��K��'�h�p)ô
J��r�h�� ��M0�F ��(f_�R5�/�//�G��+������x 1"����eS�� 5��
��:T��f��=+�7�Qɧ�\�����TE����s�༬�r���Y�s8��&�k��������#pSՊ5�M�T�b��D܊[Ng�5Q�\s��5PB@[�8ɨ�V1������&��4Wsy[�Ǿ
�w�U���2�V�����7��7��j������މd^~Yf��C��_��h;a.���&�M�
i� ��U�����Wpzs`>�/�"��'O�I����۲�y�����:�Bzd������T���q£�=й��b:���"����m�/��-/P��W�DQ�Ǵ͐��5���7������m�`�H��%A���V��!�H�ԛ׿���@"Q��z��ދ|�ߒT���-�*OU�^��Ҧ6�����!��Cw�k�|h�&Hd5�LEY�y��'�ƣ7��%�*�<C'@�l���b!wL�WW(%���C��4��������3\��������x������*������QF�Ҩ�<��m�������߃g?߉������^�)D�}�{�U��֘|�Q����=C'@�|�uwL�ׂQ�E�=�?�x+�x� "����g���S� �O�Ҩj��׈ .��fqj[��Y��Gͤ�C���焓m>{��=)���Z�%ٝ��P ���*G���]����/��8L��w��$?8��M�)\į���/#�7U�fd7'6�\h1�
vI�f�EIr���=��1�w��\�WK��VZ�HK��g�Z��͡�$m��x���� %�� �`j}�TuT���QJZ��*H>*Q�xkLFT����y��U���-�)�ôb��iA���|q`��F�'���+ ���4^Q�y �x��H)��#�t^��?@]^`A�R�S��q�jg�B:�r<h̆�Rn���z���PΦ�)��[+�n ��M�X�H!����0��I����r�� ��sKϡէU�R2��T X�gƴڳE�cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1c�n��j����ǴyƌI��xQ�q�7fM���4E�F��.���34<.�i��;��eВi���1c%9����K ���͠2��J�C��n��w��� E¤�c�F������`��5v6�%˿]�3��T��y��`�~���a���[[�J�>K�۷l<2�-4�Y��K�hgQ���L��x�V�w�P��~��M �Φ�����0l 3�ƅ�aŊIT�ȀhwJ�m��������xIM�չ��|��U7xˆS��~2�ߕ?�kW1k���C3]��;Y��nS���ґA�e�X�Yz�8,'�x�< k7Kx�����]��$��x�$�v�g�T#w��;o�����@ �z�_V���m�n|�Hֵ��h��Zg-^TAn��-� )����@4�[*�9xK��Ƌ�����j>�!,�Vt�:e�����qn8%oh���S�(2�\Q��^�aig����F��3��v�TUDV�l�Q�ꅧ�W�c��%�U��e�q�4�ҝº/�U� �$�_�Q!��>�����t�|� �,țG<t�C���[�xTXmf|��<��Oڡ�MT�|(w:���_X����j7w���t�� �
AX�ͦ�p�$�^xZ�R��������j�x����`�3=�^��ll�+˗e�Q��8g8V ��+�9M���/������o�14sn�b� ��tX�܍�s�����vE�l+@\��e�,�,�cѮ�<�(��i�HVY�r��Q� O7�a��I��>Q%d�#jUՆ�|; H��[b����ά�#������,W�s7NT1~���m&ǻ�{' \��㟾� �b�BKJ�o8�%�!���$��Q����j:��/�RX)$Sy�޳ 䍧�R��DUg_D��軦�J�\�����j�N��֖SU;~�?��O��h�ss�d��ƣ}�6�(T <��_�4���b5���� �^N ���N�%8QejF�7to��My�ө�`)g�[��/�������|����?��өJ���u�G����L�坕��/=�CTܠhd�ifH���cǞ�����G4��,��������`�D՞�{'x���G_p/5��@m +�$jV�H���3�a"��*ũ,�,��H�Jҵ�ȸ�T^Qy��o&IÉ�JUVwW���L�eM��~���3t�������A��6����r��wɤ�6���տ� ��\0H�L%L�X5�c����@�HHÃZ��|NV��+7WM��{����cig���*���ȸU���7iÉ��бz���d� *� ?�gt��X���8��̝O��X��:��]2�ɍ]�p^��++��>���A���VڛE�{��� ��DB.�&�/������56���A�rxY#ܕ�y�)��cKQtȪ��~����������! �;�C}ʃ��tf{�6��$N��Vsj ��wup�Z)zŁ�|�-�w�g+n�MVj�/d+U������~ͯ�����i���:_ix�� w��hq��r>�駃-�x�뼬)��ݷ�y��R=! ���ì:��J/l��Ik���V@�n��7�475��8�Z�� K�J�(��Ux�z�1w�)^�\�ԣ��zȪ󲦨c���2f�؍�v�+�6f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘�2��N�� o�C��\�����F1�ִ� �UZ�JV̚\�4����M����gq1z{&��Y��T
�,�HX~D� u�\��g��}x�>�+Y���dN�̮��o�l �Z�X+F��[�/j��+S~2/jV��8�J�r^�����ԉ]J}J��*ۏ<��2԰&�Jݣ�jO��M@�ѯ#0��O�[��S�������X�B^ uz�e��\�����]���d���d.���/���xXE
�f'v��O�_����H�${�%;�k�t�7ށ�m��ő|���d{a�ފ�^���Ǜ�ڎE��5ʋ��Br]W���=�_����SA���f(�0 ��oU�5�q ,�_\�l�uz�˪uz���㻲���o�=Yi���~|��
0+�=V�������J�ت ��/��ލ��zM��\�zC�L����[U�:|k*^8"��\Wٚ\ .��XTjX�5 �Sk�F�u\�1� ���q'��m�ģ/�Q���Uؕ�*�AɽDNZ׮?_�[#� ˍ4�:�^j|�5�L�G��� ||���ø�BW{6[uQF����.1��$qF��9���IHg)\������5��>C�#��u�X�Z��$�#*<�ߐ�sR�v�1Tj>J��m>*����#��(��
��[F�h�sש�5��*jQʼ�&���&��&P��犛L��[�Q��1*���� ��;����X}�I�ΰ�[Q�?�q�Q�Z H�����ݙ���֞V��EsB��C�Z9��JTK�� ����tu���p��˷�� /�O���,.k�Ud�s�OHMg4=-)�+ؿ���h2��N��w�/r|W�Qn=�GIU�;��'���j,��v��f�dz���p�e����$���V GTY�sBZ�O�1p�j:����r���"n�TUSCg��r �ve���A�ۘ��˜F�C+Ֆ#�[J���Te�'v9-�3 D�m�ӻ�u�uz������?��0�� �o��� ����h�x�u�Y�� &�����_�54�=f���07��kלU��0��]D:�����j�dw�/+��P��GUV��S��<��\2�u��at�c�^zY�R�ąmC�+��7����#��,|��:��i�N��w��*|^s��m�|�X>Ъ�^��1�\�#���͹� &���%�{,2��U��>�ݎ.c0�5�z�#�
o�g��N��O+��Q�쓭�� ������,��˗�-%K\����[S_`�y��+��b���_9��4����"�U��+��Ύap�}�I����[�M,B��.�Nt���w�H��j�漬���E� �����L��߀ 0DX(�k�ڵ���� �NoU��{�gquz
R�wkէRx'�uZ�[����3'��z�yy��ד%�<U��hN[����tz�x1� c�c���]F ݯ�B�"]a[J����Dս[cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3V�es{L+3VH ]YP�A �>�s�����ƕ���3jYF\��s���=m1�&���V���Aɼ?k\+]�6y�モ��1���gt�OIW7�a�l|1��� ����>$]e �7��؝�W�I�e?ަ�L#���>|����
�ҭ��]��
p�M5M�U�dI���61��Ԡ�eǼY�G����h�O�n��3�խR:^�k_'Yuuq#���p�#
�J�����2�x����l>��Oj������cY��馃��!�ڡ+�sZ/����� D�}��2��A�Y m����p�c#�<'x��SKx��`�*W[,e|��6�B�H)㶤kj���p��D�U(2qzx��9����*tqa�/,�
Z[�� 0�>��Ө�֜����xN)f�ă��@qը����FU՝��w(��a;ˋ�>�|T�c|�w2����eiT]*�!_\�WG{
� ��]��^���݅��Z5���t|��6�oYH�����a������O@�=��� my^ak����E�.����u��z�]#٥��hWv�(��:�,��6��A��߉J��Fa���\ �w��W���ex>v�<��?|����&i_�q�z� ���] e�R_�7�|& c*�kր4f���,J �U���_�h��\1A�������������u\��-�L\Ϝ^��~�P�hr��*tqa0��fT��:�MU��;q�>�et�u�M��Y��A>�����).,��;ɦ�C�bw�jE)��W���� ��Fӫ@�s4��e�6^�Q9oI}4�x<���.�B?��B��߫�#��$��Hx�.x9,��a!�RT�pgd5������xB��e�����.L7@�*�
Asdutt�S��VUa��RU|��I xG�߃$T�����񭟬���#_��IF�MŒ�_X�@f�o���Q�ID��I��I?|�%����$�r� ���{�����E��Nĸ�wޕ�qq�?����D�ؽ}�}o�/`ӣ�CT�i ���<Q�R{\yY�����F���QJkh�����^?Us:�E��|]���V�)Z|H�jsW��� �|�H'|��o����=d|�߼j �#��T���%�O�� W��!�N#�w�1[i�H(��SV����s�����[=�Ɉ����7���1�ȳ���T]A G�換��3����CT׻�lR�ݕCV9Q�\V#ܛ��N�ӏj�ˇ1�/�s�l�R��� %^s1���nU�j����,�x}��f��W�|JuK�w���p�����S��m,�<��7<����
��Ȼ�����[�R<&���p��?���'��,�Й��\�;����5�bH$�3�#�Q�4\���_���>�/�yw�O� �rD 9���YUD]��� Ή����@s���]���+'UaL}�� h��r�U����'7�:��sU|k)H��@����h�N�q�#�ϵ�8��y�˭�X���ű#��w��
�1!�흉�R'7��f�u�ד��0�����p�!W��ÖW+Nm�p�\����-�ioD$����g�٠˅%�%�Ð�m��V�]�̱��r�w*��Z�}��y�+L�
N��o�u�j�}�xt������)lS��tuq���x����m�NyK�U��OnDb�hf}�k�>�6��u�fT�%�����{��� <񐮸���mj��F�c�mU�ï����c��;�w��8��@dG�FUA��&��� �����=n�q�5]iP���}�z�:�k⼶��-��ʓ� Κl*'U��z�ax�W���F�dZ��zT�NR�s+��#��� w��zgi:��MB��q���t��M� �l#��^�'G�ߣ�*^�t�{����=�rE���R��n�Q�$adJl�02%��Tڊ^����<�~g�?�O�f*U�^��?��:��N�����+�o�[�P�U�s�|�Q��R']�V�-L)H �K�䐞 mY��n�\��4}Y��V�D� �h���R��;g��-��'�3aס�M� D�h�}�1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ��k�*Ț4�`L�$�b� ���U���4\dt���'���>�HȄ|�.��+Y+/�G��y���2�OCWv���3v,�'kia����� ���W���� O6߯E�=Hv
$L�l�xI��躍/��}�^]�����x��\3�� �ɮ5�� ���Q�T&G�9A�y^������i�}O��[5ޱ�wq�4,s JJ��I.myE�^�%���'V�B~�d�ׯ��}�*�j��*� ~��u��T�k���\f�KЬ�*��Y]����_v'I��˨����鑩6�X��o��'�j&u��ɧn�g��T]�o��ڌ��9����\*�wVHӖ�|� �>��:�5EF�'J ����ɝ`���!��A������ �
���e~��_;���5�ױϊ����镋m_&�O����Vi����<}"�靍��hW9�X�6��KPƣ G�"�ƭ�?��/����O�^���hC�H���L��c���i��P��j���)}���Q�Qզ��#tM��g��9� ��xGw�����~d;_�J+�RỲ�<���;�e��� ��5/Qs�/5��N[��!�a+�N�P�b+�Ѻ��I�}����-��t_�q�U=�MK�ʞ�Y���5no��*����v��v�b�ʊ{]��|� ~ Z��{-�������끇^����FVviϵ3��Ya�������=6n���dS;�-�ʹ^;�uꪪ^ �|�=��_�w+��"�����i�&4��l�#�w��i��r|W��3U�$��"J�~���O@]~t��RJV��MH�w:̦����@?��>�O���?�vdr��tS�*$�&~1>��������Z}^�n�L(��]�f*�&�*�Q��a�I����Ꝅ|��3�*����O���?�������r�?�*�4�Gyz[�k/t�k��Q��ϖ���WC�C�K�k/��x��5�|��S�*`��Ϲγ�Q����E�w���y�
o ��K�YqT�b����$����-/Pt�sZN�K��Q��*>��ݢ���U�@�Џ"JQ;���¹&�
�Lx�;+T� /+���O�赟��>� (T���?ķD^N*�'�p�����$I���W֐��W~� =��J|��_��UTe��7ְP`�;CYjk�=�s�U[��mߙ-���;�};�2|���w��o�1�p�0��~>��0���m��
@J�rǟ�cٷ4�͜��?q��\�UU�IV?2��L��/�+Шꄾ< �܇^T��� �?t�j\�Jr���Ҁ���B*�����=k�m�� ��X�,n}a����Ւ�Ia��d�p׷��l�l{\��6v8��R��ꅟ����Ҳ��f�1��F|Տ�;�e�=\D�� ,D�:ψ��r�xQ�T◎�*|{n��S
9~�=�}ӕ��G~%j�:D��j�<�ឫ:��jO%���
�$T8!j����vm��|'O��З��¹➱z\vsIv`�Ȕ�ʨj��-�^�$-��^���G �Q��{�m���`��T��#�c�֞�㸝�|n�.ߪN�$��O �������JUV���ʼ�t,�����j�g�-����mסּ�NV��� ��z��:����(�Ι*|1U�x�=�Y��k*����t� �M����N��N�DU�hK��� ؞X(刄Rv�!�#B_��c�xR����Ź���o��E5Dg>�?�f���XQ��Q�˔|@�"�աM�����veC�>��m�O$H��#]Y�� �I=���)_���`���k���*
�:a�>!X���!��W�^���wҒ��l'�<;�vwgI��t�_�?Jh��`��#E:fdx=��6Wu<�������Ӌ�d2�di���˂�c#h¬c4����?<���H���FYo��Vp�N�;�ݷJ\�� �����>�` (���t�3{�>⦊��;;q��F���x�4�Yc����S�$w�.�����d��a*k���|��Q�,��+x��s^��K߫���P^���n�O֮L5m�I�wl?-.ʲ����J8 �F���� �B.-:2��Ȕ�!����/A�#b��_m%�I�(���$|�PZ[����1�G�{^�#�����o>�3��m�w?'�cx���[�^�:W�k/�`'=���~֥���W�(�gQ���bf�v7U�z��M�3����+؍�K�:��4|G�Ct��A�+K��ʨ�{@���Ɩ �[0�5�����E�|yn4MIEND�B`�
����JFIFHH��ExifMM*V^(1f2x�i��HHPaint.NET v3.5.102012:08:13 16:16:06�0220��������(HH��C 

   ��C  ����"��
���}!1AQa"q2���#B��R��$3br�
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz���������������������������������������������������������������������������
���w!1AQaq"2�B���� #3R�br�
$4�%�&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������������������������������������������������������������������������� ?�5�� ��n��oƏ�N8 �:w����e܂ 9����S�y�[� �p#?/L���y��r9\�s���=:zЬ��鑜w:ҡ8���r:���JPprÑ�|ݻ��5[���#��g���օe���L���֜��[���w�ǭ
[����7o��:PU��r9��{�=hV\Lt��;��i���� �7��z�o*�GB<��å"�#��G9�cӧ�
ˁ鎙�qӭ`�l�����(2���#���A��T+r9\�s���=:zЬ��鑜w:Ӑ�~�ʧ�`�4Ƞ���I u�:~4�nG+��r1�ǧOZ��23��Zr��ml.8����<�TՑ�J�I��@�#��G9�cӧ�
ˁ鎙�qӭ8)$��`3�8��hR�������Ҁ���s��F3����B��zc�Fq�t�NP� ��m����օ-��ݎ�~(�܎W9�c=�N��+.�:dg�N�� ��݆ߛ���=i7��#��H��Ҁ[���#��g���քÕ����:u=�T�#�c��?� �@F�I;�9���&�<go<�;�Es�( ��3�z(��\��y��<������0�y������S��<ǵ"�gD$�Ϸ��bU�|�O ���~t��*0��1�y���J#PW��'v�>Êr�]� H<����<�@ U\Lu��;��h۴B���n���‚2@�8 t�^��(D�\�� �{���@k`mb�����o��ϔ27`c�� ׿^Fi��*��@��κ/x
Oj�8�H�A�%ۅ ��4���M�ʰ�7 1�6�{���]��<�z������]�%���At=�k�<#��O����[[��H�Pg��ηRI � �'�s@_���w��,��������s�q���޻�/�z~z| ���s�µ|��h�@��� fK�M:eDm>�bp�����V��cÚ��J�R�F:㑞 u�
&0�1��jW@��x�(�|O�38�ˈ�g�X���z���5׼)y���[��8V v�k�8���LrI�Zއo������������@"+� `��5/�C}��{�z�滿����l�yd�.�˸er�9�k�� ��΀���끜w=z�vp20@ {R�G-���I�{����Uxc�܂G���(ϺL��������h ���u���c� �HT��d�Ԟ}�8縤-���O=GS׷�z�q���QK�Y�`W<te�0N?:(@,� -����c����#<��i�Js@9�N=� z�B�(8�GQ�>�����IVNp�p�����8l��A��=��ޑ"�y`0z��;�ұ�O$)�F���Ҁ�H�l�0��?�?hge$��N2H���7;�<1c�Ԗ����\���rp:z���6mOR���YXm�_���>�F�r+x� !P�@!��s����K�]��{r~��z�<��)v� .>�Ҍ �'��z���S�
�x�Rs��i�PEPEPL0�l�s�c�O��)k\Z����1]0g�����/ď?��QqlX�Qȥ6N���澢V ��.��9����+��j-%M��Ҍl6���{���h�DeK���Nџjw�ǐ�g�|����i��1��2��G=iH6�������hH�.3��� {� x�%��$g��?�A �#nI�89���Z�aI2����Fx� �U�`�$�t���1$�G�����Q@�c'�6�;c�?:�N6�g��:���ݴ�����zv9�]���P3�^�>���)�2'�}N; �*��UJ��9��xȤ����<���� �p� ����������m޸��~4�w�Jx�>�Ԋ ��:pߧ�?�1��$�'����j7:�8\��Gb{�������������ה�̖>N�4�l�I#����u��������-Q@Q@Q@Q@ ��K}I�ڸ?��+?�y��o� năԌ�һ��6��� � ���@2Ơ�I�~`3�?
P�A�����==;� �1�rǜ������=s�v����*ylv%ߠ��]�{��ǧ�ozz0V#9Œ��{H~u�s�bx'�����6��v���� ��a��� �� w�֊j��p ��S�?>x����-�@#����F���?E�>��;%�z�;z�����($��� {t8����S�T��8��=}��ސ�!���^�Ҟ�VBNT�����������n�9Q��Zk�“�r[�����v #�C��H�^9� �E�O�9<���4�߳U���Lg38#���ף�Js�<��'�f�4������u=�ϥvȻW �:P�EQEQEQEV�KO�xN�z�I����h�OҪk���FO����9���O�M"e�F ��<q�������N�c�c����S�V��;}A$��~uYp�$n�%�?�nJS������O�J�BI���A��u�ڍ�����?7S���:P<�H�c��߯#42�1����)8������
~�#�9==�����0 ���N�����9#�q�v?�5q�0�翯�^� ��3�FO�ӧ�@������y���
AÃ���=x��S�����2q��?��ʛ�.q�y�}�hW~P3�SׁV4�V�58`�C���>��B� �����ӯ���]�� 7�<qo*��`�!�y�=h�? X��=�Lq<`���:t�$;�9�ኇ�UJ(Rp�>�¦�;s��ހ�(��(��(��(�`H�⣖�d��#*��8瑎�-6@X�Ҁ>a�Ѥ ��p�р�?��\�?.G��\�����װ~�>S=�� .��RQ����^@H�'��'-�-=y�=�X!��A����1� ��g���zI�`68�Ny�m �g�@��{ZRNy�c�ʀG˚(Dx�?��8��f�\rXr:��~\�Jo���=�h
6�0?��s�?3J[d�$F��#�9�LP)U$e@$g��ɥ�Q�7�^w Oq��@l����: ��x�GU �� ���zL�A$}� �~���I��f���/��@'��^1���A�[Ka"!�P 8Ns�W�zm�z5���)�d!#g�P�p��X��!�@�8����h�nv�G�>�(��(��(��(��"��A�ړ�m��s�����`
�� �^>)�U��<�8�������n�^�ice\�J�t��u��1��m�1�ç�_>�|�z�^�2��3�T���pA�d��Xt㟯���0�1���q�•�*�rp22O_oJ$���O ��@l�<�$���y8��h����`��<�8���FX��'��7�;{�\/F;p �?Ϟ��>�t�q���X�m��s�
p[�?�n��$N7q�2���qI��OL���Ϡ�RI��lz�H����HG �y�N;ZKK�m�S*s����W�xw��մ[U�h���~R��dO^����*�߃��zt���aK��Ͼ}?�T��?h�V*�'��� �#�������T��-��� \����������0�����oίh�'����sZ^<.�c���@[�|�7(�����S�ξ�����u���.�*��v>�J�%Iѱ�S�j}Q@Q@&��JZ�>%k2� ���uY�RS'<��@3���yHU���$~?θ�|z��<N%{�݂����ہڼ#Z�� A���,;�N���!;���zh�|]�I_붳[�[��R ������]y��ܷn|٤��;ܰ}�^�û+������Ǿ���m�d���A���\ Q�z��o���=�$'͂���:�O��h�x>S���z���{�W BO���;~��$�����I��𢏾=��?OŠ 
��zn�{��)B͐�$�
rI�~|�T ��>]��1��:j��zc�q���@!I8=2�s�{�N�'�^1�=OO�U*����k�����A� ��O_^�Oր�,���$��y'<C�R889S���h�@'��J�g��~t�Y?( �d���g�΀Sp���sϷzW8��� '��翰��#6��w$��n1��
pS�U~9r09�@��/�[\��8ی�ƽr8���@ �d����石����p�<ٝ��;c��+у��@����(��(�S�X�ÍG�3�����'\��հ�^3��P�1�B �`?~��B`��`3����Է���YP�*9P8$c�>��j���7�ۜ�Z2�����۞�O֔nH�ʂ2H_Ԋ7��RA��)����G�F��UUs�Ԏ��=:P:���<�g��Ls�m�p=�n���q����?�iх ��W8�>����6�N����NW$p[p������P��� �qH���rq�u����J����:��=z�%�m���N;`Pd(q�a��t�68���9�)���V# A���r��� ƀ��;Cu ``�׿�F��$aN@����j���<Su����E��1O��u��z�f�� q�\�s�� 1���?�p��yy��X�%��D2�a�O8�ٻ��}�~˾`��/ c�G����W�x[�v>�Kkc�S� �x��Zֈ�@�|�G�@��. .�-`E��s�V�j!@>��
2�y���(��(��|�` �Ӝc�Դ�l�yo��g�]_S���t�y0|��߶��^y�o�z��4�.�m�����;�Z�W�^�0��WXҢ֬ͼ�)8>�P�e�V�N9##'>�n݌�1���⽯ş����=Εt֓��ɔrH�?J��&׼.�f��{d?�|���qT�2���wc�?���1��r�}?�J��T���d�)$��~��w:��� o'=pOOq��E8�ˑ���� (��>b�PN?���·��V�͍��!�6�;W��g����e�-�W)�Iz�����G`��� �g�v���<G�� �ͻR��!�쌖l�~U��]��2m��9.Cz`��8���}A�N{�q��n}����K/���
:�qB9䑑���R�@ �89�<��EQEQEQEQER2�;Z( ������#���zY��x�uY1� �T�P3⏅�G�#?j��9�dC�/=k�����]���d?u\� �N��W�ȻЎ��i$�x�y�P̞!�7�h�m|��y���sӚ+��9��y9�6���d�Z(��(��(��(��(��(��(��(��(��(��(��(��(��(��(��(��(��(��(��(��(��(��(��(���
'use strict';
var eventsApp = angular.module('eventsApp', []);
/**
* @license AngularJS v1.4.0
* (c) 2010-2015 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
var $resourceMinErr = angular.$$minErr('$resource');
// Helper functions and regex to lookup a dotted path on an object
// stopping at undefined/null. The path must be composed of ASCII
// identifiers (just like $parse)
var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
function isValidDottedPath(path) {
return (path != null && path !== '' && path !== 'hasOwnProperty' &&
MEMBER_NAME_REGEX.test('.' + path));
}
function lookupDottedPath(obj, path) {
if (!isValidDottedPath(path)) {
throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
}
var keys = path.split('.');
for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) {
var key = keys[i];
obj = (obj !== null) ? obj[key] : undefined;
}
return obj;
}
/**
* Create a shallow copy of an object and clear other fields from the destination
*/
function shallowClearAndCopy(src, dst) {
dst = dst || {};
angular.forEach(dst, function(value, key) {
delete dst[key];
});
for (var key in src) {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
return dst;
}
/**
* @ngdoc module
* @name ngResource
* @description
*
* # ngResource
*
* The `ngResource` module provides interaction support with RESTful services
* via the $resource service.
*
*
* <div doc-module-components="ngResource"></div>
*
* See {@link ngResource.$resource `$resource`} for usage.
*/
/**
* @ngdoc service
* @name $resource
* @requires $http
*
* @description
* A factory which creates a resource object that lets you interact with
* [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
*
* The returned resource object has action methods which provide high-level behaviors without
* the need to interact with the low level {@link ng.$http $http} service.
*
* Requires the {@link ngResource `ngResource`} module to be installed.
*
* By default, trailing slashes will be stripped from the calculated URLs,
* which can pose problems with server backends that do not expect that
* behavior. This can be disabled by configuring the `$resourceProvider` like
* this:
*
* ```js
app.config(['$resourceProvider', function($resourceProvider) {
// Don't strip trailing slashes from calculated URLs
$resourceProvider.defaults.stripTrailingSlashes = false;
}]);
* ```
*
* @param {string} url A parameterized URL template with parameters prefixed by `:` as in
* `/user/:username`. If you are using a URL with a port number (e.g.
* `http://example.com:8080/api`), it will be respected.
*
* If you are using a url with a suffix, just add the suffix, like this:
* `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
* or even `$resource('http://example.com/resource/:resource_id.:format')`
* If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
* collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
* can escape it with `/\.`.
*
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
* `actions` methods. If any of the parameter value is a function, it will be executed every time
* when a param value needs to be obtained for a request (unless the param was overridden).
*
* Each key value in the parameter object is first bound to url template if present and then any
* excess keys are appended to the url search query after the `?`.
*
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
* URL `/path/greet?salutation=Hello`.
*
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
* from the corresponding property on the `data` object (provided when calling an action method). For
* example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
* will be `data.someProp`.
*
* @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
* the default set of resource actions. The declaration should be created in the format of {@link
* ng.$http#usage $http.config}:
*
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
* action2: {method:?, params:?, isArray:?, headers:?, ...},
* ...}
*
* Where:
*
* - **`action`** – {string} – The name of action. This name becomes the name of the method on
* your resource object.
* - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
* `DELETE`, `JSONP`, etc).
* - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
* the parameter value is a function, it will be executed every time when a param value needs to
* be obtained for a request (unless the param was overridden).
* - **`url`** – {string} – action specific `url` override. The url templating is supported just
* like for the resource-level urls.
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
* see `returns` section.
* - **`transformRequest`** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
* transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version.
* By default, transformRequest will contain one function that checks if the request data is
* an object and serializes to using `angular.toJson`. To prevent this behavior, set
* `transformRequest` to an empty array: `transformRequest: []`
* - **`transformResponse`** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
* transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version.
* By default, transformResponse will contain one function that checks if the response looks like
* a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
* `transformResponse` to an empty array: `transformResponse: []`
* - **`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.
* - **`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
* XHR object. See
* [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
* for more information.
* - **`responseType`** - `{string}` - see
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
* with `http response` object. See {@link ng.$http $http interceptors}.
*
* @param {Object} options Hash with custom settings that should extend the
* default `$resourceProvider` behavior. The only supported option is
*
* Where:
*
* - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
* slashes from any calculated URL will be stripped. (Defaults to true.)
*
* @returns {Object} A resource "class" object with methods for the default set of resource actions
* optionally extended with custom `actions`. The default set contains these actions:
* ```js
* { 'get': {method:'GET'},
* 'save': {method:'POST'},
* 'query': {method:'GET', isArray:true},
* 'remove': {method:'DELETE'},
* 'delete': {method:'DELETE'} };
* ```
*
* Calling these methods invoke an {@link ng.$http} with the specified http method,
* destination and parameters. When the data is returned from the server then the object is an
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
* read, update, delete) on server-side data like this:
* ```js
* var User = $resource('/user/:userId', {userId:'@id'});
* var user = User.get({userId:123}, function() {
* user.abc = true;
* user.$save();
* });
* ```
*
* It is important to realize that invoking a $resource object method immediately returns an
* empty reference (object or array depending on `isArray`). Once the data is returned from the
* server the existing reference is populated with the actual data. This is a useful trick since
* usually the resource is assigned to a model which is then rendered by the view. Having an empty
* object results in no rendering, once the data arrives from the server then the object is
* populated with the data and the view automatically re-renders itself showing the new data. This
* means that in most cases one never has to write a callback function for the action methods.
*
* The action methods on the class object or instance object can be invoked with the following
* parameters:
*
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
*
*
* Success callback is called with (value, responseHeaders) arguments. Error callback is called
* with (httpResponse) argument.
*
* Class actions return empty instance (with additional properties below).
* Instance actions return promise of the action.
*
* The Resource instances and collection have these additional properties:
*
* - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
* instance or collection.
*
* On success, the promise is resolved with the same resource instance or collection object,
* updated with data from server. This makes it easy to use in
* {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
* rendering until the resource(s) are loaded.
*
* On failure, the promise is resolved with the {@link ng.$http http response} object, without
* the `resource` property.
*
* If an interceptor object was provided, the promise will instead be resolved with the value
* returned by the interceptor.
*
* - `$resolved`: `true` after first server interaction is completed (either with success or
* rejection), `false` before that. Knowing if the Resource has been resolved is useful in
* data-binding.
*
* @example
*
* # Credit card resource
*
* ```js
// Define CreditCard class
var CreditCard = $resource('/user/:userId/card/:cardId',
{userId:123, cardId:'@id'}, {
charge: {method:'POST', params:{charge:true}}
});
// We can retrieve a collection from the server
var cards = CreditCard.query(function() {
// GET: /user/123/card
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
var card = cards[0];
// each item is an instance of CreditCard
expect(card instanceof CreditCard).toEqual(true);
card.name = "J. Smith";
// non GET methods are mapped onto the instances
card.$save();
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
// server returns: {id:456, number:'1234', name: 'J. Smith'};
// our custom method is mapped as well.
card.$charge({amount:9.99});
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
});
// we can create an instance as well
var newCard = new CreditCard({number:'0123'});
newCard.name = "Mike Smith";
newCard.$save();
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
// server returns: {id:789, number:'0123', name: 'Mike Smith'};
expect(newCard.id).toEqual(789);
* ```
*
* The object returned from this function execution is a resource "class" which has "static" method
* for each action in the definition.
*
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
* `headers`.
* When the data is returned from the server then the object is an instance of the resource type and
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
* operations (create, read, update, delete) on server-side data.
```js
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123}, function(user) {
user.abc = true;
user.$save();
});
```
*
* It's worth noting that the success callback for `get`, `query` and other methods gets passed
* in the response that came from the server as well as $http header getter function, so one
* could rewrite the above example and get access to http headers as:
*
```js
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123}, function(u, getResponseHeaders){
u.abc = true;
u.$save(function(u, putResponseHeaders) {
//u => saved user object
//putResponseHeaders => $http header getter
});
});
```
*
* You can also access the raw `$http` promise via the `$promise` property on the object returned
*
```
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123})
.$promise.then(function(user) {
$scope.user = user;
});
```
* # Creating a custom 'PUT' request
* In this example we create a custom method on our resource to make a PUT request
* ```js
* var app = angular.module('app', ['ngResource', 'ngRoute']);
*
* // Some APIs expect a PUT request in the format URL/object/ID
* // Here we are creating an 'update' method
* app.factory('Notes', ['$resource', function($resource) {
* return $resource('/notes/:id', null,
* {
* 'update': { method:'PUT' }
* });
* }]);
*
* // In our controller we get the ID from the URL using ngRoute and $routeParams
* // We pass in $routeParams and our Notes factory along with $scope
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
function($scope, $routeParams, Notes) {
* // First get a note object from the factory
* var note = Notes.get({ id:$routeParams.id });
* $id = note.id;
*
* // Now call update passing in the ID first then the object you are updating
* Notes.update({ id:$id }, note);
*
* // This will PUT /notes/ID with the note object in the request payload
* }]);
* ```
*/
angular.module('ngResource', ['ng']).
provider('$resource', function() {
var provider = this;
this.defaults = {
// Strip slashes by default
stripTrailingSlashes: true,
// Default actions configuration
actions: {
'get': {method: 'GET'},
'save': {method: 'POST'},
'query': {method: 'GET', isArray: true},
'remove': {method: 'DELETE'},
'delete': {method: 'DELETE'}
}
};
this.$get = ['$http', '$q', function($http, $q) {
var noop = angular.noop,
forEach = angular.forEach,
extend = angular.extend,
copy = angular.copy,
isFunction = angular.isFunction;
/**
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
* (pchar) allowed in path segments:
* segment = *pchar
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* pct-encoded = "%" HEXDIG HEXDIG
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriSegment(val) {
return encodeUriQuery(val, true).
replace(/%26/gi, '&').
replace(/%3D/gi, '=').
replace(/%2B/gi, '+');
}
/**
* This method is intended for encoding *key* or *value* parts of query component. We need a
* custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
* have to be encoded per http://tools.ietf.org/html/rfc3986:
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriQuery(val, pctEncodeSpaces) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
function Route(template, defaults) {
this.template = template;
this.defaults = extend({}, provider.defaults, defaults);
this.urlParams = {};
}
Route.prototype = {
setUrlParams: function(config, params, actionUrl) {
var self = this,
url = actionUrl || self.template,
val,
encodedVal;
var urlParams = self.urlParams = {};
forEach(url.split(/\W/), function(param) {
if (param === 'hasOwnProperty') {
throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
}
if (!(new RegExp("^\\d+$").test(param)) && param &&
(new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
urlParams[param] = true;
}
});
url = url.replace(/\\:/g, ':');
params = params || {};
forEach(self.urlParams, function(_, urlParam) {
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
if (angular.isDefined(val) && val !== null) {
encodedVal = encodeUriSegment(val);
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
return encodedVal + p1;
});
} else {
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
leadingSlashes, tail) {
if (tail.charAt(0) == '/') {
return tail;
} else {
return leadingSlashes + tail;
}
});
}
});
// strip trailing slashes and set the url (unless this behavior is specifically disabled)
if (self.defaults.stripTrailingSlashes) {
url = url.replace(/\/+$/, '') || '/';
}
// then replace collapse `/.` if found in the last URL path segment before the query
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
// replace escaped `/\.` with `/.`
config.url = url.replace(/\/\\\./, '/.');
// set params - delegate param encoding to $http
forEach(params, function(value, key) {
if (!self.urlParams[key]) {
config.params = config.params || {};
config.params[key] = value;
}
});
}
};
function resourceFactory(url, paramDefaults, actions, options) {
var route = new Route(url, options);
actions = extend({}, provider.defaults.actions, actions);
function extractParams(data, actionParams) {
var ids = {};
actionParams = extend({}, paramDefaults, actionParams);
forEach(actionParams, function(value, key) {
if (isFunction(value)) { value = value(); }
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
lookupDottedPath(data, value.substr(1)) : value;
});
return ids;
}
function defaultResponseInterceptor(response) {
return response.resource;
}
function Resource(value) {
shallowClearAndCopy(value || {}, this);
}
Resource.prototype.toJSON = function() {
var data = extend({}, this);
delete data.$promise;
delete data.$resolved;
return data;
};
forEach(actions, function(action, name) {
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
Resource[name] = function(a1, a2, a3, a4) {
var params = {}, data, success, error;
/* jshint -W086 */ /* (purposefully fall through case statements) */
switch (arguments.length) {
case 4:
error = a4;
success = a3;
//fallthrough
case 3:
case 2:
if (isFunction(a2)) {
if (isFunction(a1)) {
success = a1;
error = a2;
break;
}
success = a2;
error = a3;
//fallthrough
} else {
params = a1;
data = a2;
success = a3;
break;
}
case 1:
if (isFunction(a1)) success = a1;
else if (hasBody) data = a1;
else params = a1;
break;
case 0: break;
default:
throw $resourceMinErr('badargs',
"Expected up to 4 arguments [params, data, success, error], got {0} arguments",
arguments.length);
}
/* jshint +W086 */ /* (purposefully fall through case statements) */
var isInstanceCall = this instanceof Resource;
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
var httpConfig = {};
var responseInterceptor = action.interceptor && action.interceptor.response ||
defaultResponseInterceptor;
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
undefined;
forEach(action, function(value, key) {
if (key != 'params' && key != 'isArray' && key != 'interceptor') {
httpConfig[key] = copy(value);
}
});
if (hasBody) httpConfig.data = data;
route.setUrlParams(httpConfig,
extend({}, extractParams(data, action.params || {}), params),
action.url);
var promise = $http(httpConfig).then(function(response) {
var data = response.data,
promise = value.$promise;
if (data) {
// Need to convert action.isArray to boolean in case it is undefined
// jshint -W018
if (angular.isArray(data) !== (!!action.isArray)) {
throw $resourceMinErr('badcfg',
'Error in resource configuration for action `{0}`. Expected response to ' +
'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
}
// jshint +W018
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
if (typeof item === "object") {
value.push(new Resource(item));
} else {
// Valid JSON values may be string literals, and these should not be converted
// into objects. These items will not have access to the Resource prototype
// methods, but unfortunately there
value.push(item);
}
});
} else {
shallowClearAndCopy(data, value);
value.$promise = promise;
}
}
value.$resolved = true;
response.resource = value;
return response;
}, function(response) {
value.$resolved = true;
(error || noop)(response);
return $q.reject(response);
});
promise = promise.then(
function(response) {
var value = responseInterceptor(response);
(success || noop)(value, response.headers);
return value;
},
responseErrorInterceptor);
if (!isInstanceCall) {
// we are creating instance / collection
// - set the initial promise
// - return the instance / collection
value.$promise = promise;
value.$resolved = false;
return value;
}
// instance call
return promise;
};
Resource.prototype['$' + name] = function(params, success, error) {
if (isFunction(params)) {
error = success; success = params; params = {};
}
var result = Resource[name].call(this, params, this, success, error);
return result.$promise || result;
};
});
Resource.bind = function(additionalParamDefaults) {
return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
};
return Resource;
}
return resourceFactory;
}];
});
})(window, window.angular);
/*
AngularJS v1.4.0
(c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT
*/
(function(I,d,B){'use strict';function D(f,q){q=q||{};d.forEach(q,function(d,h){delete q[h]});for(var h in f)!f.hasOwnProperty(h)||"$"===h.charAt(0)&&"$"===h.charAt(1)||(q[h]=f[h]);return q}var x=d.$$minErr("$resource"),C=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;d.module("ngResource",["ng"]).provider("$resource",function(){var f=this;this.defaults={stripTrailingSlashes:!0,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}};
this.$get=["$http","$q",function(q,h){function u(d,g){this.template=d;this.defaults=s({},f.defaults,g);this.urlParams={}}function w(y,g,l,m){function c(b,k){var c={};k=s({},g,k);r(k,function(a,k){v(a)&&(a=a());var d;if(a&&a.charAt&&"@"==a.charAt(0)){d=b;var e=a.substr(1);if(null==e||""===e||"hasOwnProperty"===e||!C.test("."+e))throw x("badmember",e);for(var e=e.split("."),n=0,g=e.length;n<g&&d!==B;n++){var h=e[n];d=null!==d?d[h]:B}}else d=a;c[k]=d});return c}function F(b){return b.resource}function e(b){D(b||
{},this)}var G=new u(y,m);l=s({},f.defaults.actions,l);e.prototype.toJSON=function(){var b=s({},this);delete b.$promise;delete b.$resolved;return b};r(l,function(b,k){var g=/^(POST|PUT|PATCH)$/i.test(b.method);e[k]=function(a,z,m,y){var n={},f,l,A;switch(arguments.length){case 4:A=y,l=m;case 3:case 2:if(v(z)){if(v(a)){l=a;A=z;break}l=z;A=m}else{n=a;f=z;l=m;break}case 1:v(a)?l=a:g?f=a:n=a;break;case 0:break;default:throw x("badargs",arguments.length);}var u=this instanceof e,p=u?f:b.isArray?[]:new e(f),
t={},w=b.interceptor&&b.interceptor.response||F,C=b.interceptor&&b.interceptor.responseError||B;r(b,function(b,a){"params"!=a&&"isArray"!=a&&"interceptor"!=a&&(t[a]=H(b))});g&&(t.data=f);G.setUrlParams(t,s({},c(f,b.params||{}),n),b.url);n=q(t).then(function(a){var c=a.data,g=p.$promise;if(c){if(d.isArray(c)!==!!b.isArray)throw x("badcfg",k,b.isArray?"array":"object",d.isArray(c)?"array":"object",t.method,t.url);b.isArray?(p.length=0,r(c,function(a){"object"===typeof a?p.push(new e(a)):p.push(a)})):
(D(c,p),p.$promise=g)}p.$resolved=!0;a.resource=p;return a},function(a){p.$resolved=!0;(A||E)(a);return h.reject(a)});n=n.then(function(a){var b=w(a);(l||E)(b,a.headers);return b},C);return u?n:(p.$promise=n,p.$resolved=!1,p)};e.prototype["$"+k]=function(a,b,c){v(a)&&(c=b,b=a,a={});a=e[k].call(this,a,this,b,c);return a.$promise||a}});e.bind=function(b){return w(y,s({},g,b),l)};return e}var E=d.noop,r=d.forEach,s=d.extend,H=d.copy,v=d.isFunction;u.prototype={setUrlParams:function(f,g,l){var m=this,
c=l||m.template,h,e,q=m.urlParams={};r(c.split(/\W/),function(b){if("hasOwnProperty"===b)throw x("badname");!/^\d+$/.test(b)&&b&&(new RegExp("(^|[^\\\\]):"+b+"(\\W|$)")).test(c)&&(q[b]=!0)});c=c.replace(/\\:/g,":");g=g||{};r(m.urlParams,function(b,k){h=g.hasOwnProperty(k)?g[k]:m.defaults[k];d.isDefined(h)&&null!==h?(e=encodeURIComponent(h).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,
"+"),c=c.replace(new RegExp(":"+k+"(\\W|$)","g"),function(b,a){return e+a})):c=c.replace(new RegExp("(/?):"+k+"(\\W|$)","g"),function(b,a,c){return"/"==c.charAt(0)?c:a+c})});m.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");f.url=c.replace(/\/\\\./,"/.");r(g,function(b,c){m.urlParams[c]||(f.params=f.params||{},f.params[c]=b)})}};return w}]})})(window,window.angular);
//# sourceMappingURL=angular-resource.min.js.map
/**
* @license AngularJS v1.4.0
* (c) 2010-2015 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc module
* @name ngRoute
* @description
*
* # ngRoute
*
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
*
* <div doc-module-components="ngRoute"></div>
*/
/* global -ngRouteModule */
var ngRouteModule = angular.module('ngRoute', ['ng']).
provider('$route', $RouteProvider),
$routeMinErr = angular.$$minErr('ngRoute');
/**
* @ngdoc provider
* @name $routeProvider
*
* @description
*
* Used for configuring routes.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
* ## Dependencies
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*/
function $RouteProvider() {
function inherit(parent, extra) {
return angular.extend(Object.create(parent), extra);
}
var routes = {};
/**
* @ngdoc method
* @name $routeProvider#when
*
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
* contains redundant trailing slash or is missing one, the route will still match and the
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
* route definition.
*
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
* to the next slash are matched and stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain named groups starting with a colon and ending with a star:
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
*
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
* `/color/brown/largecode/code/with/slashes/edit` and extract:
*
* * `color: brown`
* * `largecode: code/with/slashes`.
*
*
* @param {Object} route Mapping information to be assigned to `$route.current` on route
* match.
*
* Object properties:
*
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with
* newly created scope or the name of a {@link angular.Module#controller registered
* controller} if passed as a string.
* - `controllerAs` – `{string=}` – An identifier name for a reference to the controller.
* If present, the controller will be published to scope under the `controllerAs` name.
* - `template` – `{string=|function()=}` – html template as a string or a function that
* returns an html template as a string which should be used by {@link
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
* This property takes precedence over `templateUrl`.
*
* If `template` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
* template that should be used by {@link ngRoute.directive:ngView ngView}.
*
* If `templateUrl` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
* be injected into the controller. If any of these dependencies are promises, the router
* will wait for them all to be resolved or one to be rejected before the controller is
* instantiated.
* If all the promises are resolved successfully, the values of the resolved promises are
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
* fired. If any of the promises are rejected the
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
* is:
*
* - `key` – `{string}`: a name of a dependency to be injected into the controller.
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
* Otherwise if function, then it is {@link auto.$injector#invoke injected}
* and the return value is treated as the dependency. If the result is a promise, it is
* resolved before its value is injected into the controller. Be aware that
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
* functions. Use `$route.current.params` to access the new route parameters, instead.
*
* - `redirectTo` – {(string|function())=} – value to update
* {@link ng.$location $location} path with and trigger route redirection.
*
* If `redirectTo` is a function, it will be called with the following parameters:
*
* - `{Object.<string>}` - route parameters extracted from the current
* `$location.path()` by applying the current route templateUrl.
* - `{string}` - current `$location.path()`
* - `{Object}` - current `$location.search()`
*
* The custom `redirectTo` function is expected to return a string which will be used
* to update `$location.path()` and `$location.search()`.
*
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
* or `$location.hash()` changes.
*
* If the option is set to `false` and url in the browser changes, then
* `$routeUpdate` event is broadcasted on the root scope.
*
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
*
* If the option is set to `true`, then the particular route can be matched without being
* case sensitive
*
* @returns {Object} self
*
* @description
* Adds a new route definition to the `$route` service.
*/
this.when = function(path, route) {
//copy original route object to preserve params inherited from proto chain
var routeCopy = angular.copy(route);
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
routeCopy.reloadOnSearch = true;
}
if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
}
routes[path] = angular.extend(
routeCopy,
path && pathRegExp(path, routeCopy)
);
// create redirection for trailing slashes
if (path) {
var redirectPath = (path[path.length - 1] == '/')
? path.substr(0, path.length - 1)
: path + '/';
routes[redirectPath] = angular.extend(
{redirectTo: path},
pathRegExp(redirectPath, routeCopy)
);
}
return this;
};
/**
* @ngdoc property
* @name $routeProvider#caseInsensitiveMatch
* @description
*
* A boolean property indicating if routes defined
* using this provider should be matched using a case insensitive
* algorithm. Defaults to `false`.
*/
this.caseInsensitiveMatch = false;
/**
* @param path {string} path
* @param opts {Object} options
* @return {?Object}
*
* @description
* Normalizes the given path, returning a regular expression
* and the original path.
*
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
*/
function pathRegExp(path, opts) {
var insensitive = opts.caseInsensitiveMatch,
ret = {
originalPath: path,
regexp: path
},
keys = ret.keys = [];
path = path
.replace(/([().])/g, '\\$1')
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
var optional = option === '?' ? option : null;
var star = option === '*' ? option : null;
keys.push({ name: key, optional: !!optional });
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (star && '(.+?)' || '([^/]+)')
+ (optional || '')
+ ')'
+ (optional || '');
})
.replace(/([\/$\*])/g, '\\$1');
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
return ret;
}
/**
* @ngdoc method
* @name $routeProvider#otherwise
*
* @description
* Sets route definition that will be used on route change when no other route definition
* is matched.
*
* @param {Object|string} params Mapping information to be assigned to `$route.current`.
* If called with a string, the value maps to `redirectTo`.
* @returns {Object} self
*/
this.otherwise = function(params) {
if (typeof params === 'string') {
params = {redirectTo: params};
}
this.when(null, params);
return this;
};
this.$get = ['$rootScope',
'$location',
'$routeParams',
'$q',
'$injector',
'$templateRequest',
'$sce',
function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
/**
* @ngdoc service
* @name $route
* @requires $location
* @requires $routeParams
*
* @property {Object} current Reference to the current route definition.
* The route definition contains:
*
* - `controller`: The controller constructor as define in route definition.
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
* controller instantiation. The `locals` contain
* the resolved values of the `resolve` map. Additionally the `locals` also contain:
*
* - `$scope` - The current route scope.
* - `$template` - The current route template HTML.
*
* @property {Object} routes Object with all route configuration Objects as its properties.
*
* @description
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
* It watches `$location.url()` and tries to map the path to an existing route definition.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
*
* The `$route` service is typically used in conjunction with the
* {@link ngRoute.directive:ngView `ngView`} directive and the
* {@link ngRoute.$routeParams `$routeParams`} service.
*
* @example
* This example shows how changing the URL hash causes the `$route` to match a route against the
* URL, and the `ngView` pulls in the partial.
*
* <example name="$route-service" module="ngRouteExample"
* deps="angular-route.js" fixBase="true">
* <file name="index.html">
* <div ng-controller="MainController">
* Choose:
* <a href="Book/Moby">Moby</a> |
* <a href="Book/Moby/ch/1">Moby: Ch1</a> |
* <a href="Book/Gatsby">Gatsby</a> |
* <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
* <a href="Book/Scarlet">Scarlet Letter</a><br/>
*
* <div ng-view></div>
*
* <hr />
*
* <pre>$location.path() = {{$location.path()}}</pre>
* <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
* <pre>$route.current.params = {{$route.current.params}}</pre>
* <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
* <pre>$routeParams = {{$routeParams}}</pre>
* </div>
* </file>
*
* <file name="book.html">
* controller: {{name}}<br />
* Book Id: {{params.bookId}}<br />
* </file>
*
* <file name="chapter.html">
* controller: {{name}}<br />
* Book Id: {{params.bookId}}<br />
* Chapter Id: {{params.chapterId}}
* </file>
*
* <file name="script.js">
* angular.module('ngRouteExample', ['ngRoute'])
*
* .controller('MainController', function($scope, $route, $routeParams, $location) {
* $scope.$route = $route;
* $scope.$location = $location;
* $scope.$routeParams = $routeParams;
* })
*
* .controller('BookController', function($scope, $routeParams) {
* $scope.name = "BookController";
* $scope.params = $routeParams;
* })
*
* .controller('ChapterController', function($scope, $routeParams) {
* $scope.name = "ChapterController";
* $scope.params = $routeParams;
* })
*
* .config(function($routeProvider, $locationProvider) {
* $routeProvider
* .when('/Book/:bookId', {
* templateUrl: 'book.html',
* controller: 'BookController',
* resolve: {
* // I will cause a 1 second delay
* delay: function($q, $timeout) {
* var delay = $q.defer();
* $timeout(delay.resolve, 1000);
* return delay.promise;
* }
* }
* })
* .when('/Book/:bookId/ch/:chapterId', {
* templateUrl: 'chapter.html',
* controller: 'ChapterController'
* });
*
* // configure html5 to get links working on jsfiddle
* $locationProvider.html5Mode(true);
* });
*
* </file>
*
* <file name="protractor.js" type="protractor">
* it('should load and compile correct template', function() {
* element(by.linkText('Moby: Ch1')).click();
* var content = element(by.css('[ng-view]')).getText();
* expect(content).toMatch(/controller\: ChapterController/);
* expect(content).toMatch(/Book Id\: Moby/);
* expect(content).toMatch(/Chapter Id\: 1/);
*
* element(by.partialLinkText('Scarlet')).click();
*
* content = element(by.css('[ng-view]')).getText();
* expect(content).toMatch(/controller\: BookController/);
* expect(content).toMatch(/Book Id\: Scarlet/);
* });
* </file>
* </example>
*/
/**
* @ngdoc event
* @name $route#$routeChangeStart
* @eventType broadcast on root scope
* @description
* Broadcasted before a route change. At this point the route services starts
* resolving all of the dependencies needed for the route change to occur.
* Typically this involves fetching the view template as well as any dependencies
* defined in `resolve` route property. Once all of the dependencies are resolved
* `$routeChangeSuccess` is fired.
*
* The route change (and the `$location` change that triggered it) can be prevented
* by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
* for more details about event object.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} next Future route information.
* @param {Route} current Current route information.
*/
/**
* @ngdoc event
* @name $route#$routeChangeSuccess
* @eventType broadcast on root scope
* @description
* Broadcasted after a route dependencies are resolved.
* {@link ngRoute.directive:ngView ngView} listens for the directive
* to instantiate the controller and render the view.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} current Current route information.
* @param {Route|Undefined} previous Previous route information, or undefined if current is
* first route entered.
*/
/**
* @ngdoc event
* @name $route#$routeChangeError
* @eventType broadcast on root scope
* @description
* Broadcasted if any of the resolve promises are rejected.
*
* @param {Object} angularEvent Synthetic event object
* @param {Route} current Current route information.
* @param {Route} previous Previous route information.
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
*/
/**
* @ngdoc event
* @name $route#$routeUpdate
* @eventType broadcast on root scope
* @description
* The `reloadOnSearch` property has been set to false, and we are reusing the same
* instance of the Controller.
*
* @param {Object} angularEvent Synthetic event object
* @param {Route} current Current/previous route information.
*/
var forceReload = false,
preparedRoute,
preparedRouteIsUpdateOnly,
$route = {
routes: routes,
/**
* @ngdoc method
* @name $route#reload
*
* @description
* Causes `$route` service to reload the current route even if
* {@link ng.$location $location} hasn't changed.
*
* As a result of that, {@link ngRoute.directive:ngView ngView}
* creates new scope and reinstantiates the controller.
*/
reload: function() {
forceReload = true;
$rootScope.$evalAsync(function() {
// Don't support cancellation of a reload for now...
prepareRoute();
commitRoute();
});
},
/**
* @ngdoc method
* @name $route#updateParams
*
* @description
* Causes `$route` service to update the current URL, replacing
* current route parameters with those specified in `newParams`.
* Provided property names that match the route's path segment
* definitions will be interpolated into the location's path, while
* remaining properties will be treated as query params.
*
* @param {!Object<string, string>} newParams mapping of URL parameter names to values
*/
updateParams: function(newParams) {
if (this.current && this.current.$$route) {
newParams = angular.extend({}, this.current.params, newParams);
$location.path(interpolate(this.current.$$route.originalPath, newParams));
// interpolate modifies newParams, only query params are left
$location.search(newParams);
} else {
throw $routeMinErr('norout', 'Tried updating route when with no current route');
}
}
};
$rootScope.$on('$locationChangeStart', prepareRoute);
$rootScope.$on('$locationChangeSuccess', commitRoute);
return $route;
/////////////////////////////////////////////////////
/**
* @param on {string} current url
* @param route {Object} route regexp to match the url against
* @return {?Object}
*
* @description
* Check if the route matches the current url.
*
* Inspired by match in
* visionmedia/express/lib/router/router.js.
*/
function switchRouteMatcher(on, route) {
var keys = route.keys,
params = {};
if (!route.regexp) return null;
var m = route.regexp.exec(on);
if (!m) return null;
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
var val = m[i];
if (key && val) {
params[key.name] = val;
}
}
return params;
}
function prepareRoute($locationEvent) {
var lastRoute = $route.current;
preparedRoute = parseRoute();
preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
&& angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
&& !preparedRoute.reloadOnSearch && !forceReload;
if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
if ($locationEvent) {
$locationEvent.preventDefault();
}
}
}
}
function commitRoute() {
var lastRoute = $route.current;
var nextRoute = preparedRoute;
if (preparedRouteIsUpdateOnly) {
lastRoute.params = nextRoute.params;
angular.copy(lastRoute.params, $routeParams);
$rootScope.$broadcast('$routeUpdate', lastRoute);
} else if (nextRoute || lastRoute) {
forceReload = false;
$route.current = nextRoute;
if (nextRoute) {
if (nextRoute.redirectTo) {
if (angular.isString(nextRoute.redirectTo)) {
$location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
.replace();
} else {
$location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
.replace();
}
}
}
$q.when(nextRoute).
then(function() {
if (nextRoute) {
var locals = angular.extend({}, nextRoute.resolve),
template, templateUrl;
angular.forEach(locals, function(value, key) {
locals[key] = angular.isString(value) ?
$injector.get(value) : $injector.invoke(value, null, null, key);
});
if (angular.isDefined(template = nextRoute.template)) {
if (angular.isFunction(template)) {
template = template(nextRoute.params);
}
} else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
if (angular.isFunction(templateUrl)) {
templateUrl = templateUrl(nextRoute.params);
}
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
if (angular.isDefined(templateUrl)) {
nextRoute.loadedTemplateUrl = templateUrl;
template = $templateRequest(templateUrl);
}
}
if (angular.isDefined(template)) {
locals['$template'] = template;
}
return $q.all(locals);
}
}).
then(function(locals) {
// after route change
if (nextRoute == $route.current) {
if (nextRoute) {
nextRoute.locals = locals;
angular.copy(nextRoute.params, $routeParams);
}
$rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
}
}, function(error) {
if (nextRoute == $route.current) {
$rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
}
});
}
}
/**
* @returns {Object} the current active route, by matching it against the URL
*/
function parseRoute() {
// Match a route
var params, match;
angular.forEach(routes, function(route, path) {
if (!match && (params = switchRouteMatcher($location.path(), route))) {
match = inherit(route, {
params: angular.extend({}, $location.search(), params),
pathParams: params});
match.$$route = route;
}
});
// No route matched; fallback to "otherwise" route
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
}
/**
* @returns {string} interpolation of the redirect path with the parameters
*/
function interpolate(string, params) {
var result = [];
angular.forEach((string || '').split(':'), function(segment, i) {
if (i === 0) {
result.push(segment);
} else {
var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
var key = segmentMatch[1];
result.push(params[key]);
result.push(segmentMatch[2] || '');
delete params[key];
}
});
return result.join('');
}
}];
}
ngRouteModule.provider('$routeParams', $RouteParamsProvider);
/**
* @ngdoc service
* @name $routeParams
* @requires $route
*
* @description
* The `$routeParams` service allows you to retrieve the current set of route parameters.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* The route parameters are a combination of {@link ng.$location `$location`}'s
* {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
*
* In case of parameter name collision, `path` params take precedence over `search` params.
*
* The service guarantees that the identity of the `$routeParams` object will remain unchanged
* (but its properties will likely change) even when a route change occurs.
*
* Note that the `$routeParams` are only updated *after* a route change completes successfully.
* This means that you cannot rely on `$routeParams` being correct in route resolve functions.
* Instead you can use `$route.current.params` to access the new route's parameters.
*
* @example
* ```js
* // Given:
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
* // Route: /Chapter/:chapterId/Section/:sectionId
* //
* // Then
* $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
* ```
*/
function $RouteParamsProvider() {
this.$get = function() { return {}; };
}
ngRouteModule.directive('ngView', ngViewFactory);
ngRouteModule.directive('ngView', ngViewFillContentFactory);
/**
* @ngdoc directive
* @name ngView
* @restrict ECA
*
* @description
* # Overview
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
* including the rendered template of the current route into the main layout (`index.html`) file.
* Every time the current route changes, the included view changes with it according to the
* configuration of the `$route` service.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* @animations
* enter - animation is used to bring new content into the browser.
* leave - animation is used to animate existing content away.
*
* The enter and leave animation occur concurrently.
*
* @scope
* @priority 400
* @param {string=} onload Expression to evaluate whenever the view updates.
*
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
* $anchorScroll} to scroll the viewport after the view is updated.
*
* - If the attribute is not set, disable scrolling.
* - If the attribute is set without value, enable scrolling.
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
* as an expression yields a truthy value.
* @example
<example name="ngView-directive" module="ngViewExample"
deps="angular-route.js;angular-animate.js"
animations="true" fixBase="true">
<file name="index.html">
<div ng-controller="MainCtrl as main">
Choose:
<a href="Book/Moby">Moby</a> |
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
<a href="Book/Gatsby">Gatsby</a> |
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
<a href="Book/Scarlet">Scarlet Letter</a><br/>
<div class="view-animate-container">
<div ng-view class="view-animate"></div>
</div>
<hr />
<pre>$location.path() = {{main.$location.path()}}</pre>
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{main.$route.current.params}}</pre>
<pre>$routeParams = {{main.$routeParams}}</pre>
</div>
</file>
<file name="book.html">
<div>
controller: {{book.name}}<br />
Book Id: {{book.params.bookId}}<br />
</div>
</file>
<file name="chapter.html">
<div>
controller: {{chapter.name}}<br />
Book Id: {{chapter.params.bookId}}<br />
Chapter Id: {{chapter.params.chapterId}}
</div>
</file>
<file name="animations.css">
.view-animate-container {
position:relative;
height:100px!important;
background:white;
border:1px solid black;
height:40px;
overflow:hidden;
}
.view-animate {
padding:10px;
}
.view-animate.ng-enter, .view-animate.ng-leave {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
display:block;
width:100%;
border-left:1px solid black;
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
padding:10px;
}
.view-animate.ng-enter {
left:100%;
}
.view-animate.ng-enter.ng-enter-active {
left:0;
}
.view-animate.ng-leave.ng-leave-active {
left:-100%;
}
</file>
<file name="script.js">
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: 'BookCtrl',
controllerAs: 'book'
})
.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: 'ChapterCtrl',
controllerAs: 'chapter'
});
$locationProvider.html5Mode(true);
}])
.controller('MainCtrl', ['$route', '$routeParams', '$location',
function($route, $routeParams, $location) {
this.$route = $route;
this.$location = $location;
this.$routeParams = $routeParams;
}])
.controller('BookCtrl', ['$routeParams', function($routeParams) {
this.name = "BookCtrl";
this.params = $routeParams;
}])
.controller('ChapterCtrl', ['$routeParams', function($routeParams) {
this.name = "ChapterCtrl";
this.params = $routeParams;
}]);
</file>
<file name="protractor.js" type="protractor">
it('should load and compile correct template', function() {
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCtrl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: BookCtrl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
</file>
</example>
*/
/**
* @ngdoc event
* @name ngView#$viewContentLoaded
* @eventType emit on the current ngView scope
* @description
* Emitted every time the ngView content is reloaded.
*/
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
function ngViewFactory($route, $anchorScroll, $animate) {
return {
restrict: 'ECA',
terminal: true,
priority: 400,
transclude: 'element',
link: function(scope, $element, attr, ctrl, $transclude) {
var currentScope,
currentElement,
previousLeaveAnimation,
autoScrollExp = attr.autoscroll,
onloadExp = attr.onload || '';
scope.$on('$routeChangeSuccess', update);
update();
function cleanupLastView() {
if (previousLeaveAnimation) {
$animate.cancel(previousLeaveAnimation);
previousLeaveAnimation = null;
}
if (currentScope) {
currentScope.$destroy();
currentScope = null;
}
if (currentElement) {
previousLeaveAnimation = $animate.leave(currentElement);
previousLeaveAnimation.then(function() {
previousLeaveAnimation = null;
});
currentElement = null;
}
}
function update() {
var locals = $route.current && $route.current.locals,
template = locals && locals.$template;
if (angular.isDefined(template)) {
var newScope = scope.$new();
var current = $route.current;
// Note: This will also link all children of ng-view that were contained in the original
// html. If that content contains controllers, ... they could pollute/change the scope.
// However, using ng-view on an element with additional content does not make sense...
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var clone = $transclude(newScope, function(clone) {
$animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
}
});
cleanupLastView();
});
currentElement = clone;
currentScope = current.scope = newScope;
currentScope.$emit('$viewContentLoaded');
currentScope.$eval(onloadExp);
} else {
cleanupLastView();
}
}
}
};
}
// This directive is called during the $transclude call of the first `ngView` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngView
// is called.
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
function ngViewFillContentFactory($compile, $controller, $route) {
return {
restrict: 'ECA',
priority: -400,
link: function(scope, $element) {
var current = $route.current,
locals = current.locals;
$element.html(locals.$template);
var link = $compile($element.contents());
if (current.controller) {
locals.$scope = scope;
var controller = $controller(current.controller, locals);
if (current.controllerAs) {
scope[current.controllerAs] = controller;
}
$element.data('$ngControllerController', controller);
$element.children().data('$ngControllerController', controller);
}
link(scope);
}
};
}
})(window, window.angular);
/*
AngularJS v1.4.0
(c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT
*/
(function(q,d,C){'use strict';function v(r,k,h){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,f,b,c,y){function z(){l&&(h.cancel(l),l=null);m&&(m.$destroy(),m=null);n&&(l=h.leave(n),l.then(function(){l=null}),n=null)}function x(){var b=r.current&&r.current.locals;if(d.isDefined(b&&b.$template)){var b=a.$new(),c=r.current;n=y(b,function(b){h.enter(b,null,n||f).then(function(){!d.isDefined(t)||t&&!a.$eval(t)||k()});z()});m=c.scope=b;m.$emit("$viewContentLoaded");
m.$eval(w)}else z()}var m,n,l,t=b.autoscroll,w=b.onload||"";a.$on("$routeChangeSuccess",x);x()}}}function A(d,k,h){return{restrict:"ECA",priority:-400,link:function(a,f){var b=h.current,c=b.locals;f.html(c.$template);var y=d(f.contents());b.controller&&(c.$scope=a,c=k(b.controller,c),b.controllerAs&&(a[b.controllerAs]=c),f.data("$ngControllerController",c),f.children().data("$ngControllerController",c));y(a)}}}q=d.module("ngRoute",["ng"]).provider("$route",function(){function r(a,f){return d.extend(Object.create(a),
f)}function k(a,d){var b=d.caseInsensitiveMatch,c={originalPath:a,regexp:a},h=c.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,d,b,c){a="?"===c?c:null;c="*"===c?c:null;h.push({name:b,optional:!!a});d=d||"";return""+(a?"":d)+"(?:"+(a?d:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");c.regexp=new RegExp("^"+a+"$",b?"i":"");return c}var h={};this.when=function(a,f){var b=d.copy(f);d.isUndefined(b.reloadOnSearch)&&(b.reloadOnSearch=!0);
d.isUndefined(b.caseInsensitiveMatch)&&(b.caseInsensitiveMatch=this.caseInsensitiveMatch);h[a]=d.extend(b,a&&k(a,b));if(a){var c="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";h[c]=d.extend({redirectTo:a},k(c,b))}return this};this.caseInsensitiveMatch=!1;this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce",function(a,f,b,c,k,q,x){function m(b){var e=s.current;
(v=(p=l())&&e&&p.$$route===e.$$route&&d.equals(p.pathParams,e.pathParams)&&!p.reloadOnSearch&&!w)||!e&&!p||a.$broadcast("$routeChangeStart",p,e).defaultPrevented&&b&&b.preventDefault()}function n(){var u=s.current,e=p;if(v)u.params=e.params,d.copy(u.params,b),a.$broadcast("$routeUpdate",u);else if(e||u)w=!1,(s.current=e)&&e.redirectTo&&(d.isString(e.redirectTo)?f.path(t(e.redirectTo,e.params)).search(e.params).replace():f.url(e.redirectTo(e.pathParams,f.path(),f.search())).replace()),c.when(e).then(function(){if(e){var a=
d.extend({},e.resolve),b,g;d.forEach(a,function(b,e){a[e]=d.isString(b)?k.get(b):k.invoke(b,null,null,e)});d.isDefined(b=e.template)?d.isFunction(b)&&(b=b(e.params)):d.isDefined(g=e.templateUrl)&&(d.isFunction(g)&&(g=g(e.params)),g=x.getTrustedResourceUrl(g),d.isDefined(g)&&(e.loadedTemplateUrl=g,b=q(g)));d.isDefined(b)&&(a.$template=b);return c.all(a)}}).then(function(c){e==s.current&&(e&&(e.locals=c,d.copy(e.params,b)),a.$broadcast("$routeChangeSuccess",e,u))},function(b){e==s.current&&a.$broadcast("$routeChangeError",
e,u,b)})}function l(){var a,b;d.forEach(h,function(c,h){var g;if(g=!b){var k=f.path();g=c.keys;var m={};if(c.regexp)if(k=c.regexp.exec(k)){for(var l=1,n=k.length;l<n;++l){var p=g[l-1],q=k[l];p&&q&&(m[p.name]=q)}g=m}else g=null;else g=null;g=a=g}g&&(b=r(c,{params:d.extend({},f.search(),a),pathParams:a}),b.$$route=c)});return b||h[null]&&r(h[null],{params:{},pathParams:{}})}function t(a,b){var c=[];d.forEach((a||"").split(":"),function(a,d){if(0===d)c.push(a);else{var f=a.match(/(\w+)(?:[?*])?(.*)/),
h=f[1];c.push(b[h]);c.push(f[2]||"");delete b[h]}});return c.join("")}var w=!1,p,v,s={routes:h,reload:function(){w=!0;a.$evalAsync(function(){m();n()})},updateParams:function(a){if(this.current&&this.current.$$route)a=d.extend({},this.current.params,a),f.path(t(this.current.$$route.originalPath,a)),f.search(a);else throw B("norout");}};a.$on("$locationChangeStart",m);a.$on("$locationChangeSuccess",n);return s}]});var B=d.$$minErr("ngRoute");q.provider("$routeParams",function(){this.$get=function(){return{}}});
q.directive("ngView",v);q.directive("ngView",A);v.$inject=["$route","$anchorScroll","$animate"];A.$inject=["$compile","$controller","$route"]})(window,window.angular);
//# sourceMappingURL=angular-route.min.js.map
/**
* @license AngularJS v1.4.0
* (c) 2010-2015 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Any commits to this file should be reviewed with security in mind. *
* Changes to this file can potentially create security vulnerabilities. *
* An approval from 2 Core members with history of modifying *
* this file is required. *
* *
* Does the change somehow allow for arbitrary javascript to be executed? *
* Or allows for someone to change the prototype of built-in objects? *
* Or gives undesired access to variables likes document or window? *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
var $sanitizeMinErr = angular.$$minErr('$sanitize');
/**
* @ngdoc module
* @name ngSanitize
* @description
*
* # ngSanitize
*
* The `ngSanitize` module provides functionality to sanitize HTML.
*
*
* <div doc-module-components="ngSanitize"></div>
*
* See {@link ngSanitize.$sanitize `$sanitize`} for usage.
*/
/*
* HTML Parser By Misko Hevery ([email protected])
* based on: HTML Parser By John Resig (ejohn.org)
* Original code by Erik Arvidsson, Mozilla Public License
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
*
* // Use like so:
* htmlParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
*/
/**
* @ngdoc service
* @name $sanitize
* @kind function
*
* @description
* The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
* then serialized back to properly escaped html string. This means that no unsafe input can make
* it into the returned string, however, since our parser is more strict than a typical browser
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
* browser, won't make it through the sanitizer. The input may also contain SVG markup.
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
*
* @param {string} html HTML input.
* @returns {string} Sanitized HTML.
*
* @example
<example module="sanitizeExample" deps="angular-sanitize.js">
<file name="index.html">
<script>
angular.module('sanitizeExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
$scope.snippet =
'<p style="color:blue">an html\n' +
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
'snippet</p>';
$scope.deliberatelyTrustDangerousSnippet = function() {
return $sce.trustAsHtml($scope.snippet);
};
}]);
</script>
<div ng-controller="ExampleController">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table>
<tr>
<td>Directive</td>
<td>How</td>
<td>Source</td>
<td>Rendered</td>
</tr>
<tr id="bind-html-with-sanitize">
<td>ng-bind-html</td>
<td>Automatically uses $sanitize</td>
<td><pre>&lt;div ng-bind-html="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind-html="snippet"></div></td>
</tr>
<tr id="bind-html-with-trust">
<td>ng-bind-html</td>
<td>Bypass $sanitize by explicitly trusting the dangerous value</td>
<td>
<pre>&lt;div ng-bind-html="deliberatelyTrustDangerousSnippet()"&gt;
&lt;/div&gt;</pre>
</td>
<td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
</tr>
<tr id="bind-default">
<td>ng-bind</td>
<td>Automatically escapes</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br/>&lt;/div&gt;</pre></td>
<td><div ng-bind="snippet"></div></td>
</tr>
</table>
</div>
</file>
<file name="protractor.js" type="protractor">
it('should sanitize the html snippet by default', function() {
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
});
it('should inline raw snippet if bound to a trusted value', function() {
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
toBe("<p style=\"color:blue\">an html\n" +
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
"snippet</p>");
});
it('should escape snippet without any filter', function() {
expect(element(by.css('#bind-default div')).getInnerHtml()).
toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
"&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
"snippet&lt;/p&gt;");
});
it('should update', function() {
element(by.model('snippet')).clear();
element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('new <b>text</b>');
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
'new <b onclick="alert(1)">text</b>');
expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
"new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
});
</file>
</example>
*/
function $SanitizeProvider() {
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
return function(html) {
var buf = [];
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
return !/^unsafe/.test($$sanitizeUri(uri, isImage));
}));
return buf.join('');
};
}];
}
function sanitizeText(chars) {
var buf = [];
var writer = htmlSanitizeWriter(buf, angular.noop);
writer.chars(chars);
return buf.join('');
}
// Regular Expressions for parsing tags and attributes
var START_TAG_REGEXP =
/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
BEGIN_TAG_REGEXP = /^</,
BEGING_END_TAGE_REGEXP = /^<\//,
COMMENT_REGEXP = /<!--(.*?)-->/g,
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
// Good source of info about elements and attributes
// http://dev.w3.org/html5/spec/Overview.html#semantics
// http://simon.html5.org/html-elements
// Safe Void Elements - HTML5
// http://dev.w3.org/html5/spec/Overview.html#void-elements
var voidElements = makeMap("area,br,col,hr,img,wbr");
// Elements that you can, intentionally, leave open (and which close themselves)
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
optionalEndTagInlineElements = makeMap("rp,rt"),
optionalEndTagElements = angular.extend({},
optionalEndTagInlineElements,
optionalEndTagBlockElements);
// Safe Block Elements - HTML5
var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
// Inline Elements - HTML5
var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
// SVG Elements
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
// They can potentially allow for arbitrary javascript to be executed. See #11290
var svgElements = makeMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
"hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
"radialGradient,rect,stop,svg,switch,text,title,tspan,use");
// Special Elements (can contain anything)
var specialElements = makeMap("script,style");
var validElements = angular.extend({},
voidElements,
blockElements,
inlineElements,
optionalEndTagElements,
svgElements);
//Attributes that have href and hence need to be sanitized
var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
'scope,scrolling,shape,size,span,start,summary,target,title,type,' +
'valign,value,vspace,width');
// SVG attributes (without "id" and "name" attributes)
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
var validAttrs = angular.extend({},
uriAttrs,
svgAttrs,
htmlAttrs);
function makeMap(str, lowercaseKeys) {
var obj = {}, items = str.split(','), i;
for (i = 0; i < items.length; i++) {
obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true;
}
return obj;
}
/**
* @example
* htmlParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
* @param {string} html string
* @param {object} handler
*/
function htmlParser(html, handler) {
if (typeof html !== 'string') {
if (html === null || typeof html === 'undefined') {
html = '';
} else {
html = '' + html;
}
}
var index, chars, match, stack = [], last = html, text;
stack.last = function() { return stack[stack.length - 1]; };
while (html) {
text = '';
chars = true;
// Make sure we're not in a script or style element
if (!stack.last() || !specialElements[stack.last()]) {
// Comment
if (html.indexOf("<!--") === 0) {
// comments containing -- are not allowed unless they terminate the comment
index = html.indexOf("--", 4);
if (index >= 0 && html.lastIndexOf("-->", index) === index) {
if (handler.comment) handler.comment(html.substring(4, index));
html = html.substring(index + 3);
chars = false;
}
// DOCTYPE
} else if (DOCTYPE_REGEXP.test(html)) {
match = html.match(DOCTYPE_REGEXP);
if (match) {
html = html.replace(match[0], '');
chars = false;
}
// end tag
} else if (BEGING_END_TAGE_REGEXP.test(html)) {
match = html.match(END_TAG_REGEXP);
if (match) {
html = html.substring(match[0].length);
match[0].replace(END_TAG_REGEXP, parseEndTag);
chars = false;
}
// start tag
} else if (BEGIN_TAG_REGEXP.test(html)) {
match = html.match(START_TAG_REGEXP);
if (match) {
// We only have a valid start-tag if there is a '>'.
if (match[4]) {
html = html.substring(match[0].length);
match[0].replace(START_TAG_REGEXP, parseStartTag);
}
chars = false;
} else {
// no ending tag found --- this piece should be encoded as an entity.
text += '<';
html = html.substring(1);
}
}
if (chars) {
index = html.indexOf("<");
text += index < 0 ? html : html.substring(0, index);
html = index < 0 ? "" : html.substring(index);
if (handler.chars) handler.chars(decodeEntities(text));
}
} else {
// IE versions 9 and 10 do not understand the regex '[^]', so using a workaround with [\W\w].
html = html.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
function(all, text) {
text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
if (handler.chars) handler.chars(decodeEntities(text));
return "";
});
parseEndTag("", stack.last());
}
if (html == last) {
throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
"of html: {0}", html);
}
last = html;
}
// Clean up any remaining tags
parseEndTag();
function parseStartTag(tag, tagName, rest, unary) {
tagName = angular.lowercase(tagName);
if (blockElements[tagName]) {
while (stack.last() && inlineElements[stack.last()]) {
parseEndTag("", stack.last());
}
}
if (optionalEndTagElements[tagName] && stack.last() == tagName) {
parseEndTag("", tagName);
}
unary = voidElements[tagName] || !!unary;
if (!unary) {
stack.push(tagName);
}
var attrs = {};
rest.replace(ATTR_REGEXP,
function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
var value = doubleQuotedValue
|| singleQuotedValue
|| unquotedValue
|| '';
attrs[name] = decodeEntities(value);
});
if (handler.start) handler.start(tagName, attrs, unary);
}
function parseEndTag(tag, tagName) {
var pos = 0, i;
tagName = angular.lowercase(tagName);
if (tagName) {
// Find the closest opened tag of the same type
for (pos = stack.length - 1; pos >= 0; pos--) {
if (stack[pos] == tagName) break;
}
}
if (pos >= 0) {
// Close all the open elements, up the stack
for (i = stack.length - 1; i >= pos; i--)
if (handler.end) handler.end(stack[i]);
// Remove the open elements from the stack
stack.length = pos;
}
}
}
var hiddenPre=document.createElement("pre");
/**
* decodes all entities into regular string
* @param value
* @returns {string} A string with decoded entities.
*/
function decodeEntities(value) {
if (!value) { return ''; }
hiddenPre.innerHTML = value.replace(/</g,"&lt;");
// innerText depends on styling as it doesn't display hidden elements.
// Therefore, it's better to use textContent not to cause unnecessary reflows.
return hiddenPre.textContent;
}
/**
* Escapes all potentially dangerous characters, so that the
* resulting string can be safely inserted into attribute or
* element text.
* @param value
* @returns {string} escaped text
*/
function encodeEntities(value) {
return value.
replace(/&/g, '&amp;').
replace(SURROGATE_PAIR_REGEXP, function(value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
}
/**
* create an HTML/XML writer which writes to buffer
* @param {Array} buf use buf.jain('') to get out sanitized html string
* @returns {object} in the form of {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* }
*/
function htmlSanitizeWriter(buf, uriValidator) {
var ignore = false;
var out = angular.bind(buf, buf.push);
return {
start: function(tag, attrs, unary) {
tag = angular.lowercase(tag);
if (!ignore && specialElements[tag]) {
ignore = tag;
}
if (!ignore && validElements[tag] === true) {
out('<');
out(tag);
angular.forEach(attrs, function(value, key) {
var lkey=angular.lowercase(key);
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
if (validAttrs[lkey] === true &&
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
out(' ');
out(key);
out('="');
out(encodeEntities(value));
out('"');
}
});
out(unary ? '/>' : '>');
}
},
end: function(tag) {
tag = angular.lowercase(tag);
if (!ignore && validElements[tag] === true) {
out('</');
out(tag);
out('>');
}
if (tag == ignore) {
ignore = false;
}
},
chars: function(chars) {
if (!ignore) {
out(encodeEntities(chars));
}
}
};
}
// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
/* global sanitizeText: false */
/**
* @ngdoc filter
* @name linky
* @kind function
*
* @description
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
* plain email address links.
*
* Requires the {@link ngSanitize `ngSanitize`} module to be installed.
*
* @param {string} text Input text.
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
* @returns {string} Html-linkified text.
*
* @usage
<span ng-bind-html="linky_expression | linky"></span>
*
* @example
<example module="linkyExample" deps="angular-sanitize.js">
<file name="index.html">
<script>
angular.module('linkyExample', ['ngSanitize'])
.controller('ExampleController', ['$scope', function($scope) {
$scope.snippet =
'Pretty text with some links:\n'+
'http://angularjs.org/,\n'+
'mailto:[email protected],\n'+
'[email protected],\n'+
'and one more: ftp://127.0.0.1/.';
$scope.snippetWithTarget = 'http://angularjs.org/';
}]);
</script>
<div ng-controller="ExampleController">
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
<table>
<tr>
<td>Filter</td>
<td>Source</td>
<td>Rendered</td>
</tr>
<tr id="linky-filter">
<td>linky filter</td>
<td>
<pre>&lt;div ng-bind-html="snippet | linky"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippet | linky"></div>
</td>
</tr>
<tr id="linky-target">
<td>linky target</td>
<td>
<pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
</td>
</tr>
<tr id="escaped-html">
<td>no filter</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
<td><div ng-bind="snippet"></div></td>
</tr>
</table>
</file>
<file name="protractor.js" type="protractor">
it('should linkify the snippet with urls', function() {
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('Pretty text with some links: http://angularjs.org/, [email protected], ' +
'[email protected], and one more: ftp://127.0.0.1/.');
expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
});
it('should not linkify snippet without the linky filter', function() {
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
toBe('Pretty text with some links: http://angularjs.org/, mailto:[email protected], ' +
'[email protected], and one more: ftp://127.0.0.1/.');
expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
});
it('should update', function() {
element(by.model('snippet')).clear();
element(by.model('snippet')).sendKeys('new http://link.');
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('new http://link.');
expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
.toBe('new http://link.');
});
it('should work with the target property', function() {
expect(element(by.id('linky-target')).
element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
toBe('http://angularjs.org/');
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
});
</file>
</example>
*/
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
var LINKY_URL_REGEXP =
/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/,
MAILTO_REGEXP = /^mailto:/;
return function(text, target) {
if (!text) return text;
var match;
var raw = text;
var html = [];
var url;
var i;
while ((match = raw.match(LINKY_URL_REGEXP))) {
// We can not end in these as they are sometimes found at the end of the sentence
url = match[0];
// if we did not match ftp/http/www/mailto then assume mailto
if (!match[2] && !match[4]) {
url = (match[3] ? 'http://' : 'mailto:') + url;
}
i = match.index;
addText(raw.substr(0, i));
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
raw = raw.substring(i + match[0].length);
}
addText(raw);
return $sanitize(html.join(''));
function addText(text) {
if (!text) {
return;
}
html.push(sanitizeText(text));
}
function addLink(url, text) {
html.push('<a ');
if (angular.isDefined(target)) {
html.push('target="',
target,
'" ');
}
html.push('href="',
url.replace(/"/g, '&quot;'),
'">');
addText(text);
html.push('</a>');
}
};
}]);
})(window, window.angular);
/*
AngularJS v1.4.0
(c) 2010-2015 Google, Inc. http://angularjs.org
License: MIT
*/
(function(n,h,p){'use strict';function E(a){var f=[];r(f,h.noop).chars(a);return f.join("")}function g(a,f){var d={},c=a.split(","),b;for(b=0;b<c.length;b++)d[f?h.lowercase(c[b]):c[b]]=!0;return d}function F(a,f){function d(a,b,d,l){b=h.lowercase(b);if(s[b])for(;e.last()&&t[e.last()];)c("",e.last());u[b]&&e.last()==b&&c("",b);(l=v[b]||!!l)||e.push(b);var m={};d.replace(G,function(b,a,f,c,d){m[a]=q(f||c||d||"")});f.start&&f.start(b,m,l)}function c(b,a){var c=0,d;if(a=h.lowercase(a))for(c=e.length-
1;0<=c&&e[c]!=a;c--);if(0<=c){for(d=e.length-1;d>=c;d--)f.end&&f.end(e[d]);e.length=c}}"string"!==typeof a&&(a=null===a||"undefined"===typeof a?"":""+a);var b,k,e=[],m=a,l;for(e.last=function(){return e[e.length-1]};a;){l="";k=!0;if(e.last()&&w[e.last()])a=a.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*"+e.last()+"[^>]*>","i"),function(a,b){b=b.replace(H,"$1").replace(I,"$1");f.chars&&f.chars(q(b));return""}),c("",e.last());else{if(0===a.indexOf("\x3c!--"))b=a.indexOf("--",4),0<=b&&a.lastIndexOf("--\x3e",
b)===b&&(f.comment&&f.comment(a.substring(4,b)),a=a.substring(b+3),k=!1);else if(x.test(a)){if(b=a.match(x))a=a.replace(b[0],""),k=!1}else if(J.test(a)){if(b=a.match(y))a=a.substring(b[0].length),b[0].replace(y,c),k=!1}else K.test(a)&&((b=a.match(z))?(b[4]&&(a=a.substring(b[0].length),b[0].replace(z,d)),k=!1):(l+="<",a=a.substring(1)));k&&(b=a.indexOf("<"),l+=0>b?a:a.substring(0,b),a=0>b?"":a.substring(b),f.chars&&f.chars(q(l)))}if(a==m)throw L("badparse",a);m=a}c()}function q(a){if(!a)return"";A.innerHTML=
a.replace(/</g,"&lt;");return A.textContent}function B(a){return a.replace(/&/g,"&amp;").replace(M,function(a){var d=a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(d-55296)+(a-56320)+65536)+";"}).replace(N,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}function r(a,f){var d=!1,c=h.bind(a,a.push);return{start:function(a,k,e){a=h.lowercase(a);!d&&w[a]&&(d=a);d||!0!==C[a]||(c("<"),c(a),h.forEach(k,function(d,e){var k=h.lowercase(e),g="img"===a&&"src"===k||
"background"===k;!0!==O[k]||!0===D[k]&&!f(d,g)||(c(" "),c(e),c('="'),c(B(d)),c('"'))}),c(e?"/>":">"))},end:function(a){a=h.lowercase(a);d||!0!==C[a]||(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||c(B(a))}}}var L=h.$$minErr("$sanitize"),z=/^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,y=/^<\/\s*([\w:-]+)[^>]*>/,G=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,K=/^</,J=/^<\//,H=/\x3c!--(.*?)--\x3e/g,x=/<!DOCTYPE([^>]*?)>/i,
I=/<!\[CDATA\[(.*?)]]\x3e/g,M=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,N=/([^\#-~| |!])/g,v=g("area,br,col,hr,img,wbr");n=g("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr");p=g("rp,rt");var u=h.extend({},p,n),s=h.extend({},n,g("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),t=h.extend({},p,g("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
n=g("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan,use");var w=g("script,style"),C=h.extend({},v,s,t,u,n),D=g("background,cite,href,longdesc,src,usemap,xlink:href");n=g("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,target,title,type,valign,value,vspace,width");
p=g("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan",
!0);var O=h.extend({},D,p,n),A=document.createElement("pre");h.module("ngSanitize",[]).provider("$sanitize",function(){this.$get=["$$sanitizeUri",function(a){return function(f){var d=[];F(f,r(d,function(c,b){return!/^unsafe/.test(a(c,b))}));return d.join("")}}]});h.module("ngSanitize").filter("linky",["$sanitize",function(a){var f=/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/,d=/^mailto:/;return function(c,b){function k(a){a&&g.push(E(a))}function e(a,c){g.push("<a ");
h.isDefined(b)&&g.push('target="',b,'" ');g.push('href="',a.replace(/"/g,"&quot;"),'">');k(c);g.push("</a>")}if(!c)return c;for(var m,l=c,g=[],n,p;m=l.match(f);)n=m[0],m[2]||m[4]||(n=(m[3]?"http://":"mailto:")+n),p=m.index,k(l.substr(0,p)),e(n,m[0].replace(d,"")),l=l.substring(p+m[0].length);k(l);return a(g.join(""))}}])})(window,window.angular);
//# sourceMappingURL=angular-sanitize.min.js.map

Angular Event Registration — Demo Application for Angular JS

This project is a sample application demonstrating a more fully developed realistic single page angular application.

Server

Two Servers are provided, an ASP.NET MVC server, and a node server. If using the ASP.NET server, simply start it up in IIS. If using the node server, from the command line run "npm install" and then either "server.sh" if you are on a linux box or "server.bat" if you are on a windows box.

Running the application

Navigate your browser to http://localhost:8000/ to see the app running in your browser. You may have to add the page to the url such as EventDetails.html depending on where you are at in the course. The port may be configured either in the node server or on IIS.

This file has been truncated, but you can view the full file.
/**
* @license AngularJS v1.4.0
* (c) 2010-2015 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, document, undefined) {'use strict';
/**
* @description
*
* This object provides a utility for producing rich Error messages within
* Angular. It can be called as follows:
*
* var exampleMinErr = minErr('example');
* throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
*
* The above creates an instance of minErr in the example namespace. The
* resulting error will have a namespaced error code of example.one. The
* resulting error will replace {0} with the value of foo, and {1} with the
* value of bar. The object is not restricted in the number of arguments it can
* take.
*
* If fewer arguments are specified than necessary for interpolation, the extra
* interpolation markers will be preserved in the final string.
*
* Since data will be parsed statically during a build step, some restrictions
* are applied with respect to how minErr instances are created and called.
* Instances should have names of the form namespaceMinErr for a minErr created
* using minErr('namespace') . Error codes, namespaces and template strings
* should all be static strings, not variables or general expressions.
*
* @param {string} module The namespace to use for the new minErr instance.
* @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
* error from returned function, for cases when a particular type of error is useful.
* @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
*/
function minErr(module, ErrorConstructor) {
ErrorConstructor = ErrorConstructor || Error;
return function() {
var SKIP_INDEXES = 2;
var templateArgs = arguments,
code = templateArgs[0],
message = '[' + (module ? module + ':' : '') + code + '] ',
template = templateArgs[1],
paramPrefix, i;
message += template.replace(/\{\d+\}/g, function(match) {
var index = +match.slice(1, -1),
shiftedIndex = index + SKIP_INDEXES;
if (shiftedIndex < templateArgs.length) {
return toDebugString(templateArgs[shiftedIndex]);
}
return match;
});
message += '\nhttp://errors.angularjs.org/1.4.0/' +
(module ? module + '/' : '') + code;
for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
encodeURIComponent(toDebugString(templateArgs[i]));
}
return new ErrorConstructor(message);
};
}
/* We need to tell jshint what variables are being exported */
/* global angular: true,
msie: true,
jqLite: true,
jQuery: true,
slice: true,
splice: true,
push: true,
toString: true,
ngMinErr: true,
angularModule: true,
uid: true,
REGEX_STRING_REGEXP: true,
VALIDITY_STATE_PROPERTY: true,
lowercase: true,
uppercase: true,
manualLowercase: true,
manualUppercase: true,
nodeName_: true,
isArrayLike: true,
forEach: true,
forEachSorted: true,
reverseParams: true,
nextUid: true,
setHashKey: true,
extend: true,
toInt: true,
inherit: true,
merge: true,
noop: true,
identity: true,
valueFn: true,
isUndefined: true,
isDefined: true,
isObject: true,
isBlankObject: true,
isString: true,
isNumber: true,
isDate: true,
isArray: true,
isFunction: true,
isRegExp: true,
isWindow: true,
isScope: true,
isFile: true,
isFormData: true,
isBlob: true,
isBoolean: true,
isPromiseLike: true,
trim: true,
escapeForRegexp: true,
isElement: true,
makeMap: true,
includes: true,
arrayRemove: true,
copy: true,
shallowCopy: true,
equals: true,
csp: true,
jq: true,
concat: true,
sliceArgs: true,
bind: true,
toJsonReplacer: true,
toJson: true,
fromJson: true,
convertTimezoneToLocal: true,
timezoneToOffset: true,
startingTag: true,
tryDecodeURIComponent: true,
parseKeyValue: true,
toKeyValue: true,
encodeUriSegment: true,
encodeUriQuery: true,
angularInit: true,
bootstrap: true,
getTestability: true,
snake_case: true,
bindJQuery: true,
assertArg: true,
assertArgFn: true,
assertNotHasOwnProperty: true,
getter: true,
getBlockNodes: true,
hasOwnProperty: true,
createMap: true,
NODE_TYPE_ELEMENT: true,
NODE_TYPE_ATTRIBUTE: true,
NODE_TYPE_TEXT: true,
NODE_TYPE_COMMENT: true,
NODE_TYPE_DOCUMENT: true,
NODE_TYPE_DOCUMENT_FRAGMENT: true,
*/
////////////////////////////////////
/**
* @ngdoc module
* @name ng
* @module ng
* @description
*
* # ng (core module)
* The ng module is loaded by default when an AngularJS application is started. The module itself
* contains the essential components for an AngularJS application to function. The table below
* lists a high level breakdown of each of the services/factories, filters, directives and testing
* components available within this core module.
*
* <div doc-module-components="ng"></div>
*/
var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
// The name of a form control's ValidityState property.
// This is used so that it's possible for internal tests to create mock ValidityStates.
var VALIDITY_STATE_PROPERTY = 'validity';
/**
* @ngdoc function
* @name angular.lowercase
* @module ng
* @kind function
*
* @description Converts the specified string to lowercase.
* @param {string} string String to be converted to lowercase.
* @returns {string} Lowercased string.
*/
var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* @ngdoc function
* @name angular.uppercase
* @module ng
* @kind function
*
* @description Converts the specified string to uppercase.
* @param {string} string String to be converted to uppercase.
* @returns {string} Uppercased string.
*/
var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
var manualLowercase = function(s) {
/* jshint bitwise: false */
return isString(s)
? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
: s;
};
var manualUppercase = function(s) {
/* jshint bitwise: false */
return isString(s)
? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
: s;
};
// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
// with correct but slower alternatives.
if ('i' !== 'I'.toLowerCase()) {
lowercase = manualLowercase;
uppercase = manualUppercase;
}
var
msie, // holds major version number for IE, or NaN if UA is not IE.
jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding
slice = [].slice,
splice = [].splice,
push = [].push,
toString = Object.prototype.toString,
getPrototypeOf = Object.getPrototypeOf,
ngMinErr = minErr('ng'),
/** @name angular */
angular = window.angular || (window.angular = {}),
angularModule,
uid = 0;
/**
* documentMode is an IE-only property
* http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
*/
msie = document.documentMode;
/**
* @private
* @param {*} obj
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
* String ...)
*/
function isArrayLike(obj) {
if (obj == null || isWindow(obj)) {
return false;
}
// Support: iOS 8.2 (not reproducible in simulator)
// "length" in obj used to prevent JIT error (gh-11508)
var length = "length" in Object(obj) && obj.length;
if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
return true;
}
return isString(obj) || isArray(obj) || length === 0 ||
typeof length === 'number' && length > 0 && (length - 1) in obj;
}
/**
* @ngdoc function
* @name angular.forEach
* @module ng
* @kind function
*
* @description
* Invokes the `iterator` function once for each item in `obj` collection, which can be either an
* object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
* is the value of an object property or an array element, `key` is the object property key or
* array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
*
* It is worth noting that `.forEach` does not iterate over inherited properties because it filters
* using the `hasOwnProperty` method.
*
* Unlike ES262's
* [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
* Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
* return the value provided.
*
```js
var values = {name: 'misko', gender: 'male'};
var log = [];
angular.forEach(values, function(value, key) {
this.push(key + ': ' + value);
}, log);
expect(log).toEqual(['name: misko', 'gender: male']);
```
*
* @param {Object|Array} obj Object to iterate over.
* @param {Function} iterator Iterator function.
* @param {Object=} context Object to become context (`this`) for the iterator function.
* @returns {Object|Array} Reference to `obj`.
*/
function forEach(obj, iterator, context) {
var key, length;
if (obj) {
if (isFunction(obj)) {
for (key in obj) {
// Need to check if hasOwnProperty exists,
// as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
iterator.call(context, obj[key], key, obj);
}
}
} else if (isArray(obj) || isArrayLike(obj)) {
var isPrimitive = typeof obj !== 'object';
for (key = 0, length = obj.length; key < length; key++) {
if (isPrimitive || key in obj) {
iterator.call(context, obj[key], key, obj);
}
}
} else if (obj.forEach && obj.forEach !== forEach) {
obj.forEach(iterator, context, obj);
} else if (isBlankObject(obj)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in obj) {
iterator.call(context, obj[key], key, obj);
}
} else if (typeof obj.hasOwnProperty === 'function') {
// Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
for (key in obj) {
if (obj.hasOwnProperty(key)) {
iterator.call(context, obj[key], key, obj);
}
}
} else {
// Slow path for objects which do not have a method `hasOwnProperty`
for (key in obj) {
if (hasOwnProperty.call(obj, key)) {
iterator.call(context, obj[key], key, obj);
}
}
}
}
return obj;
}
function forEachSorted(obj, iterator, context) {
var keys = Object.keys(obj).sort();
for (var i = 0; i < keys.length; i++) {
iterator.call(context, obj[keys[i]], keys[i]);
}
return keys;
}
/**
* when using forEach the params are value, key, but it is often useful to have key, value.
* @param {function(string, *)} iteratorFn
* @returns {function(*, string)}
*/
function reverseParams(iteratorFn) {
return function(value, key) { iteratorFn(key, value); };
}
/**
* A consistent way of creating unique IDs in angular.
*
* Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
* we hit number precision issues in JavaScript.
*
* Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
*
* @returns {number} an unique alpha-numeric string
*/
function nextUid() {
return ++uid;
}
/**
* Set or clear the hashkey for an object.
* @param obj object
* @param h the hashkey (!truthy to delete the hashkey)
*/
function setHashKey(obj, h) {
if (h) {
obj.$$hashKey = h;
} else {
delete obj.$$hashKey;
}
}
function baseExtend(dst, objs, deep) {
var h = dst.$$hashKey;
for (var i = 0, ii = objs.length; i < ii; ++i) {
var obj = objs[i];
if (!isObject(obj) && !isFunction(obj)) continue;
var keys = Object.keys(obj);
for (var j = 0, jj = keys.length; j < jj; j++) {
var key = keys[j];
var src = obj[key];
if (deep && isObject(src)) {
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
} else {
dst[key] = src;
}
}
}
setHashKey(dst, h);
return dst;
}
/**
* @ngdoc function
* @name angular.extend
* @module ng
* @kind function
*
* @description
* Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
* by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
*
* **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
* {@link angular.merge} for this.
*
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
* @returns {Object} Reference to `dst`.
*/
function extend(dst) {
return baseExtend(dst, slice.call(arguments, 1), false);
}
/**
* @ngdoc function
* @name angular.merge
* @module ng
* @kind function
*
* @description
* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
*
* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
* objects, performing a deep copy.
*
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
* @returns {Object} Reference to `dst`.
*/
function merge(dst) {
return baseExtend(dst, slice.call(arguments, 1), true);
}
function toInt(str) {
return parseInt(str, 10);
}
function inherit(parent, extra) {
return extend(Object.create(parent), extra);
}
/**
* @ngdoc function
* @name angular.noop
* @module ng
* @kind function
*
* @description
* A function that performs no operations. This function can be useful when writing code in the
* functional style.
```js
function foo(callback) {
var result = calculateResult();
(callback || angular.noop)(result);
}
```
*/
function noop() {}
noop.$inject = [];
/**
* @ngdoc function
* @name angular.identity
* @module ng
* @kind function
*
* @description
* A function that returns its first argument. This function is useful when writing code in the
* functional style.
*
```js
function transformer(transformationFn, value) {
return (transformationFn || angular.identity)(value);
};
```
* @param {*} value to be returned.
* @returns {*} the value passed in.
*/
function identity($) {return $;}
identity.$inject = [];
function valueFn(value) {return function() {return value;};}
/**
* @ngdoc function
* @name angular.isUndefined
* @module ng
* @kind function
*
* @description
* Determines if a reference is undefined.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is undefined.
*/
function isUndefined(value) {return typeof value === 'undefined';}
/**
* @ngdoc function
* @name angular.isDefined
* @module ng
* @kind function
*
* @description
* Determines if a reference is defined.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is defined.
*/
function isDefined(value) {return typeof value !== 'undefined';}
/**
* @ngdoc function
* @name angular.isObject
* @module ng
* @kind function
*
* @description
* Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
* considered to be objects. Note that JavaScript arrays are objects.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Object` but not `null`.
*/
function isObject(value) {
// http://jsperf.com/isobject4
return value !== null && typeof value === 'object';
}
/**
* Determine if a value is an object with a null prototype
*
* @returns {boolean} True if `value` is an `Object` with a null prototype
*/
function isBlankObject(value) {
return value !== null && typeof value === 'object' && !getPrototypeOf(value);
}
/**
* @ngdoc function
* @name angular.isString
* @module ng
* @kind function
*
* @description
* Determines if a reference is a `String`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `String`.
*/
function isString(value) {return typeof value === 'string';}
/**
* @ngdoc function
* @name angular.isNumber
* @module ng
* @kind function
*
* @description
* Determines if a reference is a `Number`.
*
* This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
*
* If you wish to exclude these then you can use the native
* [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
* method.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Number`.
*/
function isNumber(value) {return typeof value === 'number';}
/**
* @ngdoc function
* @name angular.isDate
* @module ng
* @kind function
*
* @description
* Determines if a value is a date.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Date`.
*/
function isDate(value) {
return toString.call(value) === '[object Date]';
}
/**
* @ngdoc function
* @name angular.isArray
* @module ng
* @kind function
*
* @description
* Determines if a reference is an `Array`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Array`.
*/
var isArray = Array.isArray;
/**
* @ngdoc function
* @name angular.isFunction
* @module ng
* @kind function
*
* @description
* Determines if a reference is a `Function`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Function`.
*/
function isFunction(value) {return typeof value === 'function';}
/**
* Determines if a value is a regular expression object.
*
* @private
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `RegExp`.
*/
function isRegExp(value) {
return toString.call(value) === '[object RegExp]';
}
/**
* Checks if `obj` is a window object.
*
* @private
* @param {*} obj Object to check
* @returns {boolean} True if `obj` is a window obj.
*/
function isWindow(obj) {
return obj && obj.window === obj;
}
function isScope(obj) {
return obj && obj.$evalAsync && obj.$watch;
}
function isFile(obj) {
return toString.call(obj) === '[object File]';
}
function isFormData(obj) {
return toString.call(obj) === '[object FormData]';
}
function isBlob(obj) {
return toString.call(obj) === '[object Blob]';
}
function isBoolean(value) {
return typeof value === 'boolean';
}
function isPromiseLike(obj) {
return obj && isFunction(obj.then);
}
var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
function isTypedArray(value) {
return TYPED_ARRAY_REGEXP.test(toString.call(value));
}
var trim = function(value) {
return isString(value) ? value.trim() : value;
};
// Copied from:
// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
// Prereq: s is a string.
var escapeForRegexp = function(s) {
return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
replace(/\x08/g, '\\x08');
};
/**
* @ngdoc function
* @name angular.isElement
* @module ng
* @kind function
*
* @description
* Determines if a reference is a DOM element (or wrapped jQuery element).
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
*/
function isElement(node) {
return !!(node &&
(node.nodeName // we are a direct element
|| (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
}
/**
* @param str 'key1,key2,...'
* @returns {object} in the form of {key1:true, key2:true, ...}
*/
function makeMap(str) {
var obj = {}, items = str.split(","), i;
for (i = 0; i < items.length; i++) {
obj[items[i]] = true;
}
return obj;
}
function nodeName_(element) {
return lowercase(element.nodeName || (element[0] && element[0].nodeName));
}
function includes(array, obj) {
return Array.prototype.indexOf.call(array, obj) != -1;
}
function arrayRemove(array, value) {
var index = array.indexOf(value);
if (index >= 0) {
array.splice(index, 1);
}
return index;
}
/**
* @ngdoc function
* @name angular.copy
* @module ng
* @kind function
*
* @description
* Creates a deep copy of `source`, which should be an object or an array.
*
* * If no destination is supplied, a copy of the object or array is created.
* * If a destination is provided, all of its elements (for arrays) or properties (for objects)
* are deleted and then all elements/properties from the source are copied to it.
* * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
* * If `source` is identical to 'destination' an exception will be thrown.
*
* @param {*} source The source that will be used to make a copy.
* Can be any type, including primitives, `null`, and `undefined`.
* @param {(Object|Array)=} destination Destination into which the source is copied. If
* provided, must be of the same type as `source`.
* @returns {*} The copy or updated `destination`, if `destination` was specified.
*
* @example
<example module="copyExample">
<file name="index.html">
<div ng-controller="ExampleController">
<form novalidate class="simple-form">
Name: <input type="text" ng-model="user.name" /><br />
E-mail: <input type="email" ng-model="user.email" /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
<script>
angular.module('copyExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.master= {};
$scope.update = function(user) {
// Example with 1 argument
$scope.master= angular.copy(user);
};
$scope.reset = function() {
// Example with 2 arguments
angular.copy($scope.master, $scope.user);
};
$scope.reset();
}]);
</script>
</file>
</example>
*/
function copy(source, destination, stackSource, stackDest) {
if (isWindow(source) || isScope(source)) {
throw ngMinErr('cpws',
"Can't copy! Making copies of Window or Scope instances is not supported.");
}
if (isTypedArray(destination)) {
throw ngMinErr('cpta',
"Can't copy! TypedArray destination cannot be mutated.");
}
if (!destination) {
destination = source;
if (source) {
if (isArray(source)) {
destination = copy(source, [], stackSource, stackDest);
} else if (isTypedArray(source)) {
destination = new source.constructor(source);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
} else if (isObject(source)) {
var emptyObject = Object.create(getPrototypeOf(source));
destination = copy(source, emptyObject, stackSource, stackDest);
}
}
} else {
if (source === destination) throw ngMinErr('cpi',
"Can't copy! Source and destination are identical.");
stackSource = stackSource || [];
stackDest = stackDest || [];
if (isObject(source)) {
var index = stackSource.indexOf(source);
if (index !== -1) return stackDest[index];
stackSource.push(source);
stackDest.push(destination);
}
var result, key;
if (isArray(source)) {
destination.length = 0;
for (var i = 0; i < source.length; i++) {
result = copy(source[i], null, stackSource, stackDest);
if (isObject(source[i])) {
stackSource.push(source[i]);
stackDest.push(result);
}
destination.push(result);
}
} else {
var h = destination.$$hashKey;
if (isArray(destination)) {
destination.length = 0;
} else {
forEach(destination, function(value, key) {
delete destination[key];
});
}
if (isBlankObject(source)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in source) {
putValue(key, source[key], destination, stackSource, stackDest);
}
} else if (source && typeof source.hasOwnProperty === 'function') {
// Slow path, which must rely on hasOwnProperty
for (key in source) {
if (source.hasOwnProperty(key)) {
putValue(key, source[key], destination, stackSource, stackDest);
}
}
} else {
// Slowest path --- hasOwnProperty can't be called as a method
for (key in source) {
if (hasOwnProperty.call(source, key)) {
putValue(key, source[key], destination, stackSource, stackDest);
}
}
}
setHashKey(destination,h);
}
}
return destination;
function putValue(key, val, destination, stackSource, stackDest) {
// No context allocation, trivial outer scope, easily inlined
var result = copy(val, null, stackSource, stackDest);
if (isObject(val)) {
stackSource.push(val);
stackDest.push(result);
}
destination[key] = result;
}
}
/**
* Creates a shallow copy of an object, an array or a primitive.
*
* Assumes that there are no proto properties for objects.
*/
function shallowCopy(src, dst) {
if (isArray(src)) {
dst = dst || [];
for (var i = 0, ii = src.length; i < ii; i++) {
dst[i] = src[i];
}
} else if (isObject(src)) {
dst = dst || {};
for (var key in src) {
if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
}
return dst || src;
}
/**
* @ngdoc function
* @name angular.equals
* @module ng
* @kind function
*
* @description
* Determines if two objects or two values are equivalent. Supports value types, regular
* expressions, arrays and objects.
*
* Two objects or values are considered equivalent if at least one of the following is true:
*
* * Both objects or values pass `===` comparison.
* * Both objects or values are of the same type and all of their properties are equal by
* comparing them with `angular.equals`.
* * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
* * Both values represent the same regular expression (In JavaScript,
* /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
* representation matches).
*
* During a property comparison, properties of `function` type and properties with names
* that begin with `$` are ignored.
*
* Scope and DOMWindow objects are being compared only by identify (`===`).
*
* @param {*} o1 Object or value to compare.
* @param {*} o2 Object or value to compare.
* @returns {boolean} True if arguments are equal.
*/
function equals(o1, o2) {
if (o1 === o2) return true;
if (o1 === null || o2 === null) return false;
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
if (t1 == t2) {
if (t1 == 'object') {
if (isArray(o1)) {
if (!isArray(o2)) return false;
if ((length = o1.length) == o2.length) {
for (key = 0; key < length; key++) {
if (!equals(o1[key], o2[key])) return false;
}
return true;
}
} else if (isDate(o1)) {
if (!isDate(o2)) return false;
return equals(o1.getTime(), o2.getTime());
} else if (isRegExp(o1)) {
return isRegExp(o2) ? o1.toString() == o2.toString() : false;
} else {
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
keySet = createMap();
for (key in o1) {
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
if (!equals(o1[key], o2[key])) return false;
keySet[key] = true;
}
for (key in o2) {
if (!(key in keySet) &&
key.charAt(0) !== '$' &&
o2[key] !== undefined &&
!isFunction(o2[key])) return false;
}
return true;
}
}
}
return false;
}
var csp = function() {
if (isDefined(csp.isActive_)) return csp.isActive_;
var active = !!(document.querySelector('[ng-csp]') ||
document.querySelector('[data-ng-csp]'));
if (!active) {
try {
/* jshint -W031, -W054 */
new Function('');
/* jshint +W031, +W054 */
} catch (e) {
active = true;
}
}
return (csp.isActive_ = active);
};
/**
* @ngdoc directive
* @module ng
* @name ngJq
*
* @element ANY
* @param {string=} ngJq the name of the library available under `window`
* to be used for angular.element
* @description
* Use this directive to force the angular.element library. This should be
* used to force either jqLite by leaving ng-jq blank or setting the name of
* the jquery variable under window (eg. jQuery).
*
* Since angular looks for this directive when it is loaded (doesn't wait for the
* DOMContentLoaded event), it must be placed on an element that comes before the script
* which loads angular. Also, only the first instance of `ng-jq` will be used and all
* others ignored.
*
* @example
* This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
```html
<!doctype html>
<html ng-app ng-jq>
...
...
</html>
```
* @example
* This example shows how to use a jQuery based library of a different name.
* The library name must be available at the top most 'window'.
```html
<!doctype html>
<html ng-app ng-jq="jQueryLib">
...
...
</html>
```
*/
var jq = function() {
if (isDefined(jq.name_)) return jq.name_;
var el;
var i, ii = ngAttrPrefixes.length, prefix, name;
for (i = 0; i < ii; ++i) {
prefix = ngAttrPrefixes[i];
if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
name = el.getAttribute(prefix + 'jq');
break;
}
}
return (jq.name_ = name);
};
function concat(array1, array2, index) {
return array1.concat(slice.call(array2, index));
}
function sliceArgs(args, startIndex) {
return slice.call(args, startIndex || 0);
}
/* jshint -W101 */
/**
* @ngdoc function
* @name angular.bind
* @module ng
* @kind function
*
* @description
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
* `fn`). You can supply optional `args` that are prebound to the function. This feature is also
* known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
* distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
*
* @param {Object} self Context which `fn` should be evaluated in.
* @param {function()} fn Function to be bound.
* @param {...*} args Optional arguments to be prebound to the `fn` function call.
* @returns {function()} Function that wraps the `fn` with all the specified bindings.
*/
/* jshint +W101 */
function bind(self, fn) {
var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
if (isFunction(fn) && !(fn instanceof RegExp)) {
return curryArgs.length
? function() {
return arguments.length
? fn.apply(self, concat(curryArgs, arguments, 0))
: fn.apply(self, curryArgs);
}
: function() {
return arguments.length
? fn.apply(self, arguments)
: fn.call(self);
};
} else {
// in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
return fn;
}
}
function toJsonReplacer(key, value) {
var val = value;
if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
val = undefined;
} else if (isWindow(value)) {
val = '$WINDOW';
} else if (value && document === value) {
val = '$DOCUMENT';
} else if (isScope(value)) {
val = '$SCOPE';
}
return val;
}
/**
* @ngdoc function
* @name angular.toJson
* @module ng
* @kind function
*
* @description
* Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
* stripped since angular uses this notation internally.
*
* @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
* @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
* If set to an integer, the JSON output will contain that many spaces per indentation.
* @returns {string|undefined} JSON-ified string representing `obj`.
*/
function toJson(obj, pretty) {
if (typeof obj === 'undefined') return undefined;
if (!isNumber(pretty)) {
pretty = pretty ? 2 : null;
}
return JSON.stringify(obj, toJsonReplacer, pretty);
}
/**
* @ngdoc function
* @name angular.fromJson
* @module ng
* @kind function
*
* @description
* Deserializes a JSON string.
*
* @param {string} json JSON string to deserialize.
* @returns {Object|Array|string|number} Deserialized JSON string.
*/
function fromJson(json) {
return isString(json)
? JSON.parse(json)
: json;
}
function timezoneToOffset(timezone, fallback) {
var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
}
function addDateMinutes(date, minutes) {
date = new Date(date.getTime());
date.setMinutes(date.getMinutes() + minutes);
return date;
}
function convertTimezoneToLocal(date, timezone, reverse) {
reverse = reverse ? -1 : 1;
var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
}
/**
* @returns {string} Returns the string representation of the element.
*/
function startingTag(element) {
element = jqLite(element).clone();
try {
// turns out IE does not let you set .html() on elements which
// are not allowed to have children. So we just ignore it.
element.empty();
} catch (e) {}
var elemHtml = jqLite('<div>').append(element).html();
try {
return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
elemHtml.
match(/^(<[^>]+>)/)[1].
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
} catch (e) {
return lowercase(elemHtml);
}
}
/////////////////////////////////////////////////
/**
* Tries to decode the URI component without throwing an exception.
*
* @private
* @param str value potential URI component to check.
* @returns {boolean} True if `value` can be decoded
* with the decodeURIComponent function.
*/
function tryDecodeURIComponent(value) {
try {
return decodeURIComponent(value);
} catch (e) {
// Ignore any invalid uri component
}
}
/**
* Parses an escaped url query string into key-value pairs.
* @returns {Object.<string,boolean|Array>}
*/
function parseKeyValue(/**string*/keyValue) {
var obj = {}, key_value, key;
forEach((keyValue || "").split('&'), function(keyValue) {
if (keyValue) {
key_value = keyValue.replace(/\+/g,'%20').split('=');
key = tryDecodeURIComponent(key_value[0]);
if (isDefined(key)) {
var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
if (!hasOwnProperty.call(obj, key)) {
obj[key] = val;
} else if (isArray(obj[key])) {
obj[key].push(val);
} else {
obj[key] = [obj[key],val];
}
}
}
});
return obj;
}
function toKeyValue(obj) {
var parts = [];
forEach(obj, function(value, key) {
if (isArray(value)) {
forEach(value, function(arrayValue) {
parts.push(encodeUriQuery(key, true) +
(arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
});
} else {
parts.push(encodeUriQuery(key, true) +
(value === true ? '' : '=' + encodeUriQuery(value, true)));
}
});
return parts.length ? parts.join('&') : '';
}
/**
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
* segments:
* segment = *pchar
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* pct-encoded = "%" HEXDIG HEXDIG
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriSegment(val) {
return encodeUriQuery(val, true).
replace(/%26/gi, '&').
replace(/%3D/gi, '=').
replace(/%2B/gi, '+');
}
/**
* This method is intended for encoding *key* or *value* parts of query component. We need a custom
* method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
* encoded per http://tools.ietf.org/html/rfc3986:
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriQuery(val, pctEncodeSpaces) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%3B/gi, ';').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
function getNgAttribute(element, ngAttr) {
var attr, i, ii = ngAttrPrefixes.length;
for (i = 0; i < ii; ++i) {
attr = ngAttrPrefixes[i] + ngAttr;
if (isString(attr = element.getAttribute(attr))) {
return attr;
}
}
return null;
}
/**
* @ngdoc directive
* @name ngApp
* @module ng
*
* @element ANY
* @param {angular.Module} ngApp an optional application
* {@link angular.module module} name to load.
* @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
* created in "strict-di" mode. This means that the application will fail to invoke functions which
* do not use explicit function annotation (and are thus unsuitable for minification), as described
* in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
* tracking down the root of these bugs.
*
* @description
*
* Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
* designates the **root element** of the application and is typically placed near the root element
* of the page - e.g. on the `<body>` or `<html>` 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.
*
* 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
* should contain the application code needed or have dependencies on other modules that will
* contain the code. See {@link angular.module} for more information.
*
* In the example below if the `ngApp` directive were not placed on the `html` element then the
* document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
* would not be resolved to `3`.
*
* `ngApp` is the easiest, and most common way to bootstrap an application.
*
<example module="ngAppDemo">
<file name="index.html">
<div ng-controller="ngAppDemoController">
I can add: {{a}} + {{b}} = {{ a+b }}
</div>
</file>
<file name="script.js">
angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
$scope.a = 1;
$scope.b = 2;
});
</file>
</example>
*
* Using `ngStrictDi`, you would see something like this:
*
<example ng-app-included="true">
<file name="index.html">
<div ng-app="ngAppStrictDemo" ng-strict-di>
<div ng-controller="GoodController1">
I can add: {{a}} + {{b}} = {{ a+b }}
<p>This renders because the controller does not fail to
instantiate, by using explicit annotation style (see
script.js for details)
</p>
</div>
<div ng-controller="GoodController2">
Name: <input ng-model="name"><br />
Hello, {{name}}!
<p>This renders because the controller does not fail to
instantiate, by using explicit annotation style
(see script.js for details)
</p>
</div>
<div ng-controller="BadController">
I can add: {{a}} + {{b}} = {{ a+b }}
<p>The controller could not be instantiated, due to relying
on automatic function annotations (which are disabled in
strict mode). As such, the content of this section is not
interpolated, and there should be an error in your web console.
</p>
</div>
</div>
</file>
<file name="script.js">
angular.module('ngAppStrictDemo', [])
// BadController will fail to instantiate, due to relying on automatic function annotation,
// rather than an explicit annotation
.controller('BadController', function($scope) {
$scope.a = 1;
$scope.b = 2;
})
// Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
// due to using explicit annotations using the array style and $inject property, respectively.
.controller('GoodController1', ['$scope', function($scope) {
$scope.a = 1;
$scope.b = 2;
}])
.controller('GoodController2', GoodController2);
function GoodController2($scope) {
$scope.name = "World";
}
GoodController2.$inject = ['$scope'];
</file>
<file name="style.css">
div[ng-controller] {
margin-bottom: 1em;
-webkit-border-radius: 4px;
border-radius: 4px;
border: 1px solid;
padding: .5em;
}
div[ng-controller^=Good] {
border-color: #d6e9c6;
background-color: #dff0d8;
color: #3c763d;
}
div[ng-controller^=Bad] {
border-color: #ebccd1;
background-color: #f2dede;
color: #a94442;
margin-bottom: 0;
}
</file>
</example>
*/
function angularInit(element, bootstrap) {
var appElement,
module,
config = {};
// The element `element` has priority over any other element
forEach(ngAttrPrefixes, function(prefix) {
var name = prefix + 'app';
if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
appElement = element;
module = element.getAttribute(name);
}
});
forEach(ngAttrPrefixes, function(prefix) {
var name = prefix + 'app';
var candidate;
if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
appElement = candidate;
module = candidate.getAttribute(name);
}
});
if (appElement) {
config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
bootstrap(appElement, module ? [module] : [], config);
}
}
/**
* @ngdoc function
* @name angular.bootstrap
* @module ng
* @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}.
*
* 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.
*
* ```html
* <!doctype html>
* <html>
* <body>
* <div ng-controller="WelcomeController">
* {{greeting}}
* </div>
*
* <script src="angular.js"></script>
* <script>
* var app = angular.module('demo', [])
* .controller('WelcomeController', function($scope) {
* $scope.greeting = 'Welcome!';
* });
* angular.bootstrap(document, ['demo']);
* </script>
* </body>
* </html>
* ```
*
* @param {DOMElement} element DOM element which is the root of angular application.
* @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
* Each item in the array should be the name of a predefined module or a (DI annotated)
* function that will be invoked by the injector as a `config` block.
* See: {@link angular.module modules}
* @param {Object=} config an object for defining configuration options for the application. The
* following keys are supported:
*
* * `strictDi` - disable automatic function annotation for the application. This is meant to
* assist in finding bugs which break minified code. Defaults to `false`.
*
* @returns {auto.$injector} Returns the newly created injector for this app.
*/
function bootstrap(element, modules, config) {
if (!isObject(config)) config = {};
var defaultConfig = {
strictDi: false
};
config = extend(defaultConfig, config);
var doBootstrap = function() {
element = jqLite(element);
if (element.injector()) {
var tag = (element[0] === document) ? 'document' : startingTag(element);
//Encode angle brackets to prevent input from being sanitized to empty string #8683
throw ngMinErr(
'btstrpd',
"App Already Bootstrapped with this Element '{0}'",
tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
}
modules = modules || [];
modules.unshift(['$provide', function($provide) {
$provide.value('$rootElement', element);
}]);
if (config.debugInfoEnabled) {
// Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
modules.push(['$compileProvider', function($compileProvider) {
$compileProvider.debugInfoEnabled(true);
}]);
}
modules.unshift('ng');
var injector = createInjector(modules, config.strictDi);
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
function bootstrapApply(scope, element, compile, injector) {
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
}]
);
return injector;
};
var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
config.debugInfoEnabled = true;
window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
}
if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
return doBootstrap();
}
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
angular.resumeBootstrap = function(extraModules) {
forEach(extraModules, function(module) {
modules.push(module);
});
return doBootstrap();
};
if (isFunction(angular.resumeDeferredBootstrap)) {
angular.resumeDeferredBootstrap();
}
}
/**
* @ngdoc function
* @name angular.reloadWithDebugInfo
* @module ng
* @description
* Use this function to reload the current application with debug information turned on.
* This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
*
* See {@link ng.$compileProvider#debugInfoEnabled} for more.
*/
function reloadWithDebugInfo() {
window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
window.location.reload();
}
/**
* @name angular.getTestability
* @module ng
* @description
* Get the testability service for the instance of Angular on the given
* element.
* @param {DOMElement} element DOM element which is the root of angular application.
*/
function getTestability(rootElement) {
var injector = angular.element(rootElement).injector();
if (!injector) {
throw ngMinErr('test',
'no injector found for element argument to getTestability');
}
return injector.get('$$testability');
}
var SNAKE_CASE_REGEXP = /[A-Z]/g;
function snake_case(name, separator) {
separator = separator || '_';
return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
}
var bindJQueryFired = false;
var skipDestroyOnNextJQueryCleanData;
function bindJQuery() {
var originalCleanData;
if (bindJQueryFired) {
return;
}
// bind to jQuery if present;
var jqName = jq();
jQuery = window.jQuery; // use default jQuery.
if (isDefined(jqName)) { // `ngJq` present
jQuery = jqName === null ? undefined : window[jqName]; // if empty; use jqLite. if not empty, use jQuery specified by `ngJq`.
}
// Use jQuery if it exists with proper functionality, otherwise default to us.
// Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
// Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
// versions. It will not work for sure with jQuery <1.7, though.
if (jQuery && jQuery.fn.on) {
jqLite = jQuery;
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
isolateScope: JQLitePrototype.isolateScope,
controller: JQLitePrototype.controller,
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
// All nodes removed from the DOM via various jQuery APIs like .remove()
// are passed through jQuery.cleanData. Monkey-patch this method to fire
// the $destroy event on all removed nodes.
originalCleanData = jQuery.cleanData;
jQuery.cleanData = function(elems) {
var events;
if (!skipDestroyOnNextJQueryCleanData) {
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
events = jQuery._data(elem, "events");
if (events && events.$destroy) {
jQuery(elem).triggerHandler('$destroy');
}
}
} else {
skipDestroyOnNextJQueryCleanData = false;
}
originalCleanData(elems);
};
} else {
jqLite = JQLite;
}
angular.element = jqLite;
// Prevent double-proxying.
bindJQueryFired = true;
}
/**
* throw error if the argument is falsy.
*/
function assertArg(arg, name, reason) {
if (!arg) {
throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
}
return arg;
}
function assertArgFn(arg, name, acceptArrayAnnotation) {
if (acceptArrayAnnotation && isArray(arg)) {
arg = arg[arg.length - 1];
}
assertArg(isFunction(arg), name, 'not a function, got ' +
(arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
return arg;
}
/**
* throw error if the name given is hasOwnProperty
* @param {String} name the name to test
* @param {String} context the context in which the name is used, such as module or directive
*/
function assertNotHasOwnProperty(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
}
}
/**
* Return the value accessible from the object by path. Any undefined traversals are ignored
* @param {Object} obj starting object
* @param {String} path path to traverse
* @param {boolean} [bindFnToScope=true]
* @returns {Object} value as accessible by path
*/
//TODO(misko): this function needs to be removed
function getter(obj, path, bindFnToScope) {
if (!path) return obj;
var keys = path.split('.');
var key;
var lastInstance = obj;
var len = keys.length;
for (var i = 0; i < len; i++) {
key = keys[i];
if (obj) {
obj = (lastInstance = obj)[key];
}
}
if (!bindFnToScope && isFunction(obj)) {
return bind(lastInstance, obj);
}
return obj;
}
/**
* Return the DOM siblings between the first and last node in the given array.
* @param {Array} array like object
* @returns {jqLite} jqLite collection containing the nodes
*/
function getBlockNodes(nodes) {
// TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
// collection, otherwise update the original collection.
var node = nodes[0];
var endNode = nodes[nodes.length - 1];
var blockNodes = [node];
do {
node = node.nextSibling;
if (!node) break;
blockNodes.push(node);
} while (node !== endNode);
return jqLite(blockNodes);
}
/**
* Creates a new object without a prototype. This object is useful for lookup without having to
* guard against prototypically inherited properties via hasOwnProperty.
*
* Related micro-benchmarks:
* - http://jsperf.com/object-create2
* - http://jsperf.com/proto-map-lookup/2
* - http://jsperf.com/for-in-vs-object-keys2
*
* @returns {Object}
*/
function createMap() {
return Object.create(null);
}
var NODE_TYPE_ELEMENT = 1;
var NODE_TYPE_ATTRIBUTE = 2;
var NODE_TYPE_TEXT = 3;
var NODE_TYPE_COMMENT = 8;
var NODE_TYPE_DOCUMENT = 9;
var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
/**
* @ngdoc type
* @name angular.Module
* @module ng
* @description
*
* Interface for configuring angular {@link angular.module modules}.
*/
function setupModuleLoader(window) {
var $injectorMinErr = minErr('$injector');
var ngMinErr = minErr('ng');
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}
var angular = ensure(window, 'angular', Object);
// We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
angular.$$minErr = angular.$$minErr || minErr;
return ensure(angular, 'module', function() {
/** @type {Object.<string, angular.Module>} */
var modules = {};
/**
* @ngdoc function
* @name angular.module
* @module ng
* @description
*
* The `angular.module` is a global place for creating, registering and retrieving Angular
* modules.
* All modules (angular core or 3rd party) that should be available to an application must be
* registered using this mechanism.
*
* When passed two or more arguments, a new module is created. If passed only one argument, an
* existing module (the name passed as the first argument to `module`) is retrieved.
*
*
* # Module
*
* A module is a collection of services, directives, controllers, filters, and configuration information.
* `angular.module` is used to configure the {@link auto.$injector $injector}.
*
* ```js
* // Create a new module
* var myModule = angular.module('myModule', []);
*
* // register a new service
* myModule.value('appName', 'MyCoolApp');
*
* // configure existing services inside initialization blocks.
* myModule.config(['$locationProvider', function($locationProvider) {
* // Configure existing providers
* $locationProvider.hashPrefix('!');
* }]);
* ```
*
* Then you can create an injector and load your modules like this:
*
* ```js
* var injector = angular.injector(['ng', 'myModule'])
* ```
*
* However it's more likely that you'll just use
* {@link ng.directive:ngApp ngApp} or
* {@link angular.bootstrap} to simplify this process for you.
*
* @param {!string} name The name of the module to create or retrieve.
* @param {!Array.<string>=} requires If specified then new module is being created. If
* unspecified then the module is being retrieved for further configuration.
* @param {Function=} configFn Optional configuration function for the module. Same as
* {@link angular.Module#config Module#config()}.
* @returns {module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
}
};
assertNotHasOwnProperty(name, 'module');
if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}
return ensure(modules, name, function() {
if (!requires) {
throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
"the module name or forgot to load it. If registering a module ensure that you " +
"specify the dependencies as the second argument.", name);
}
/** @type {!Array.<Array.<*>>} */
var invokeQueue = [];
/** @type {!Array.<Function>} */
var configBlocks = [];
/** @type {!Array.<Function>} */
var runBlocks = [];
var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
/** @type {angular.Module} */
var moduleInstance = {
// Private state
_invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks,
/**
* @ngdoc property
* @name angular.Module#requires
* @module ng
*
* @description
* Holds the list of modules which the injector will load before the current module is
* loaded.
*/
requires: requires,
/**
* @ngdoc property
* @name angular.Module#name
* @module ng
*
* @description
* Name of the module.
*/
name: name,
/**
* @ngdoc method
* @name angular.Module#provider
* @module ng
* @param {string} name service name
* @param {Function} providerType Construction function for creating new instance of the
* service.
* @description
* See {@link auto.$provide#provider $provide.provider()}.
*/
provider: invokeLater('$provide', 'provider'),
/**
* @ngdoc method
* @name angular.Module#factory
* @module ng
* @param {string} name service name
* @param {Function} providerFunction Function for creating new instance of the service.
* @description
* See {@link auto.$provide#factory $provide.factory()}.
*/
factory: invokeLater('$provide', 'factory'),
/**
* @ngdoc method
* @name angular.Module#service
* @module ng
* @param {string} name service name
* @param {Function} constructor A constructor function that will be instantiated.
* @description
* See {@link auto.$provide#service $provide.service()}.
*/
service: invokeLater('$provide', 'service'),
/**
* @ngdoc method
* @name angular.Module#value
* @module ng
* @param {string} name service name
* @param {*} object Service instance object.
* @description
* See {@link auto.$provide#value $provide.value()}.
*/
value: invokeLater('$provide', 'value'),
/**
* @ngdoc method
* @name angular.Module#constant
* @module ng
* @param {string} name constant name
* @param {*} object Constant value.
* @description
* Because the constant are fixed, they get applied before other provide methods.
* See {@link auto.$provide#constant $provide.constant()}.
*/
constant: invokeLater('$provide', 'constant', 'unshift'),
/**
* @ngdoc method
* @name angular.Module#decorator
* @module ng
* @param {string} The name of the service to decorate.
* @param {Function} This function will be invoked when the service needs to be
* instantiated and should return the decorated service instance.
* @description
* See {@link auto.$provide#decorator $provide.decorator()}.
*/
decorator: invokeLater('$provide', 'decorator'),
/**
* @ngdoc method
* @name angular.Module#animation
* @module ng
* @param {string} name animation name
* @param {Function} animationFactory Factory function for creating new instance of an
* animation.
* @description
*
* **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
*
*
* Defines an animation hook that can be later used with
* {@link $animate $animate} service and directives that use this service.
*
* ```js
* module.animation('.animation-name', function($inject1, $inject2) {
* return {
* eventName : function(element, done) {
* //code to run the animation
* //once complete, then run done()
* return function cancellationFunction(element) {
* //code to cancel the animation
* }
* }
* }
* })
* ```
*
* See {@link ng.$animateProvider#register $animateProvider.register()} and
* {@link ngAnimate ngAnimate module} for more information.
*/
animation: invokeLater('$animateProvider', 'register'),
/**
* @ngdoc method
* @name angular.Module#filter
* @module ng
* @param {string} name Filter name - this must be a valid angular expression identifier
* @param {Function} filterFactory Factory function for creating new instance of filter.
* @description
* See {@link ng.$filterProvider#register $filterProvider.register()}.
*
* <div class="alert alert-warning">
* **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
* Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
* your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
* (`myapp_subsection_filterx`).
* </div>
*/
filter: invokeLater('$filterProvider', 'register'),
/**
* @ngdoc method
* @name angular.Module#controller
* @module ng
* @param {string|Object} name Controller name, or an object map of controllers where the
* keys are the names and the values are the constructors.
* @param {Function} constructor Controller constructor function.
* @description
* See {@link ng.$controllerProvider#register $controllerProvider.register()}.
*/
controller: invokeLater('$controllerProvider', 'register'),
/**
* @ngdoc method
* @name angular.Module#directive
* @module ng
* @param {string|Object} name Directive name, or an object map of directives where the
* keys are the names and the values are the factories.
* @param {Function} directiveFactory Factory function for creating new instance of
* directives.
* @description
* See {@link ng.$compileProvider#directive $compileProvider.directive()}.
*/
directive: invokeLater('$compileProvider', 'directive'),
/**
* @ngdoc method
* @name angular.Module#config
* @module ng
* @param {Function} configFn Execute this function on module load. Useful for service
* configuration.
* @description
* Use this method to register work which needs to be performed on module loading.
* For more about how to configure services, see
* {@link providers#provider-recipe Provider Recipe}.
*/
config: config,
/**
* @ngdoc method
* @name angular.Module#run
* @module ng
* @param {Function} initializationFn Execute this function after injector creation.
* Useful for application initialization.
* @description
* Use this method to register work which should be performed when the injector is done
* loading all modules.
*/
run: function(block) {
runBlocks.push(block);
return this;
}
};
if (configFn) {
config(configFn);
}
return moduleInstance;
/**
* @param {string} provider
* @param {string} method
* @param {String=} insertMethod
* @returns {angular.Module}
*/
function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() {
queue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance;
};
}
});
};
});
}
/* global: toDebugString: true */
function serializeObject(obj) {
var seen = [];
return JSON.stringify(obj, function(key, val) {
val = toJsonReplacer(key, val);
if (isObject(val)) {
if (seen.indexOf(val) >= 0) return '<<already seen>>';
seen.push(val);
}
return val;
});
}
function toDebugString(obj) {
if (typeof obj === 'function') {
return obj.toString().replace(/ \{[\s\S]*$/, '');
} else if (typeof obj === 'undefined') {
return 'undefined';
} else if (typeof obj !== 'string') {
return serializeObject(obj);
}
return obj;
}
/* global angularModule: true,
version: true,
$LocaleProvider,
$CompileProvider,
htmlAnchorDirective,
inputDirective,
inputDirective,
formDirective,
scriptDirective,
selectDirective,
styleDirective,
optionDirective,
ngBindDirective,
ngBindHtmlDirective,
ngBindTemplateDirective,
ngClassDirective,
ngClassEvenDirective,
ngClassOddDirective,
ngCspDirective,
ngCloakDirective,
ngControllerDirective,
ngFormDirective,
ngHideDirective,
ngIfDirective,
ngIncludeDirective,
ngIncludeFillContentDirective,
ngInitDirective,
ngNonBindableDirective,
ngPluralizeDirective,
ngRepeatDirective,
ngShowDirective,
ngStyleDirective,
ngSwitchDirective,
ngSwitchWhenDirective,
ngSwitchDefaultDirective,
ngOptionsDirective,
ngTranscludeDirective,
ngModelDirective,
ngListDirective,
ngChangeDirective,
patternDirective,
patternDirective,
requiredDirective,
requiredDirective,
minlengthDirective,
minlengthDirective,
maxlengthDirective,
maxlengthDirective,
ngValueDirective,
ngModelOptionsDirective,
ngAttributeAliasDirectives,
ngEventDirectives,
$AnchorScrollProvider,
$AnimateProvider,
$$CoreAnimateQueueProvider,
$$CoreAnimateRunnerProvider,
$BrowserProvider,
$CacheFactoryProvider,
$ControllerProvider,
$DocumentProvider,
$ExceptionHandlerProvider,
$FilterProvider,
$InterpolateProvider,
$IntervalProvider,
$$HashMapProvider,
$HttpProvider,
$HttpParamSerializerProvider,
$HttpParamSerializerJQLikeProvider,
$HttpBackendProvider,
$LocationProvider,
$LogProvider,
$ParseProvider,
$RootScopeProvider,
$QProvider,
$$QProvider,
$$SanitizeUriProvider,
$SceProvider,
$SceDelegateProvider,
$SnifferProvider,
$TemplateCacheProvider,
$TemplateRequestProvider,
$$TestabilityProvider,
$TimeoutProvider,
$$RAFProvider,
$$AsyncCallbackProvider,
$WindowProvider,
$$jqLiteProvider,
$$CookieReaderProvider
*/
/**
* @ngdoc object
* @name angular.version
* @module ng
* @description
* An object that contains information about the current AngularJS version. This object has the
* following properties:
*
* - `full` – `{string}` – Full version string, such as "0.9.18".
* - `major` – `{number}` – Major version number, such as "0".
* - `minor` – `{number}` – Minor version number, such as "9".
* - `dot` – `{number}` – Dot version number, such as "18".
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
full: '1.4.0', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 4,
dot: 0,
codeName: 'jaracimrman-existence'
};
function publishExternalAPI(angular) {
extend(angular, {
'bootstrap': bootstrap,
'copy': copy,
'extend': extend,
'merge': merge,
'equals': equals,
'element': jqLite,
'forEach': forEach,
'injector': createInjector,
'noop': noop,
'bind': bind,
'toJson': toJson,
'fromJson': fromJson,
'identity': identity,
'isUndefined': isUndefined,
'isDefined': isDefined,
'isString': isString,
'isFunction': isFunction,
'isObject': isObject,
'isNumber': isNumber,
'isElement': isElement,
'isArray': isArray,
'version': version,
'isDate': isDate,
'lowercase': lowercase,
'uppercase': uppercase,
'callbacks': {counter: 0},
'getTestability': getTestability,
'$$minErr': minErr,
'$$csp': csp,
'reloadWithDebugInfo': reloadWithDebugInfo
});
angularModule = setupModuleLoader(window);
try {
angularModule('ngLocale');
} catch (e) {
angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
}
angularModule('ng', ['ngLocale'], ['$provide',
function ngModule($provide) {
// $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
$provide.provider({
$$sanitizeUri: $$SanitizeUriProvider
});
$provide.provider('$compile', $CompileProvider).
directive({
a: htmlAnchorDirective,
input: inputDirective,
textarea: inputDirective,
form: formDirective,
script: scriptDirective,
select: selectDirective,
style: styleDirective,
option: optionDirective,
ngBind: ngBindDirective,
ngBindHtml: ngBindHtmlDirective,
ngBindTemplate: ngBindTemplateDirective,
ngClass: ngClassDirective,
ngClassEven: ngClassEvenDirective,
ngClassOdd: ngClassOddDirective,
ngCloak: ngCloakDirective,
ngController: ngControllerDirective,
ngForm: ngFormDirective,
ngHide: ngHideDirective,
ngIf: ngIfDirective,
ngInclude: ngIncludeDirective,
ngInit: ngInitDirective,
ngNonBindable: ngNonBindableDirective,
ngPluralize: ngPluralizeDirective,
ngRepeat: ngRepeatDirective,
ngShow: ngShowDirective,
ngStyle: ngStyleDirective,
ngSwitch: ngSwitchDirective,
ngSwitchWhen: ngSwitchWhenDirective,
ngSwitchDefault: ngSwitchDefaultDirective,
ngOptions: ngOptionsDirective,
ngTransclude: ngTranscludeDirective,
ngModel: ngModelDirective,
ngList: ngListDirective,
ngChange: ngChangeDirective,
pattern: patternDirective,
ngPattern: patternDirective,
required: requiredDirective,
ngRequired: requiredDirective,
minlength: minlengthDirective,
ngMinlength: minlengthDirective,
maxlength: maxlengthDirective,
ngMaxlength: maxlengthDirective,
ngValue: ngValueDirective,
ngModelOptions: ngModelOptionsDirective
}).
directive({
ngInclude: ngIncludeFillContentDirective
}).
directive(ngAttributeAliasDirectives).
directive(ngEventDirectives);
$provide.provider({
$anchorScroll: $AnchorScrollProvider,
$animate: $AnimateProvider,
$$animateQueue: $$CoreAnimateQueueProvider,
$$AnimateRunner: $$CoreAnimateRunnerProvider,
$browser: $BrowserProvider,
$cacheFactory: $CacheFactoryProvider,
$controller: $ControllerProvider,
$document: $DocumentProvider,
$exceptionHandler: $ExceptionHandlerProvider,
$filter: $FilterProvider,
$interpolate: $InterpolateProvider,
$interval: $IntervalProvider,
$http: $HttpProvider,
$httpParamSerializer: $HttpParamSerializerProvider,
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
$httpBackend: $HttpBackendProvider,
$location: $LocationProvider,
$log: $LogProvider,
$parse: $ParseProvider,
$rootScope: $RootScopeProvider,
$q: $QProvider,
$$q: $$QProvider,
$sce: $SceProvider,
$sceDelegate: $SceDelegateProvider,
$sniffer: $SnifferProvider,
$templateCache: $TemplateCacheProvider,
$templateRequest: $TemplateRequestProvider,
$$testability: $$TestabilityProvider,
$timeout: $TimeoutProvider,
$window: $WindowProvider,
$$rAF: $$RAFProvider,
$$asyncCallback: $$AsyncCallbackProvider,
$$jqLite: $$jqLiteProvider,
$$HashMap: $$HashMapProvider,
$$cookieReader: $$CookieReaderProvider
});
}
]);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Any commits to this file should be reviewed with security in mind. *
* Changes to this file can potentially create security vulnerabilities. *
* An approval from 2 Core members with history of modifying *
* this file is required. *
* *
* Does the change somehow allow for arbitrary javascript to be executed? *
* Or allows for someone to change the prototype of built-in objects? *
* Or gives undesired access to variables likes document or window? *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* global JQLitePrototype: true,
addEventListenerFn: true,
removeEventListenerFn: true,
BOOLEAN_ATTR: true,
ALIASED_ATTR: true,
*/
//////////////////////////////////
//JQLite
//////////////////////////////////
/**
* @ngdoc function
* @name angular.element
* @module ng
* @kind function
*
* @description
* Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
*
* If jQuery is available, `angular.element` is an alias for the
* [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
* delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
*
* <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
* Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
* commonly needed functionality with the goal of having a very small footprint.</div>
*
* To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
*
* <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
* jqLite; they are never raw DOM references.</div>
*
* ## Angular's jqLite
* jqLite provides only the following jQuery methods:
*
* - [`addClass()`](http://api.jquery.com/addClass/)
* - [`after()`](http://api.jquery.com/after/)
* - [`append()`](http://api.jquery.com/append/)
* - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
* - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
* - [`clone()`](http://api.jquery.com/clone/)
* - [`contents()`](http://api.jquery.com/contents/)
* - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
* - [`data()`](http://api.jquery.com/data/)
* - [`detach()`](http://api.jquery.com/detach/)
* - [`empty()`](http://api.jquery.com/empty/)
* - [`eq()`](http://api.jquery.com/eq/)
* - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
* - [`hasClass()`](http://api.jquery.com/hasClass/)
* - [`html()`](http://api.jquery.com/html/)
* - [`next()`](http://api.jquery.com/next/) - Does not support selectors
* - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
* - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
* - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
* - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
* - [`prepend()`](http://api.jquery.com/prepend/)
* - [`prop()`](http://api.jquery.com/prop/)
* - [`ready()`](http://api.jquery.com/ready/)
* - [`remove()`](http://api.jquery.com/remove/)
* - [`removeAttr()`](http://api.jquery.com/removeAttr/)
* - [`removeClass()`](http://api.jquery.com/removeClass/)
* - [`removeData()`](http://api.jquery.com/removeData/)
* - [`replaceWith()`](http://api.jquery.com/replaceWith/)
* - [`text()`](http://api.jquery.com/text/)
* - [`toggleClass()`](http://api.jquery.com/toggleClass/)
* - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
* - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
* - [`val()`](http://api.jquery.com/val/)
* - [`wrap()`](http://api.jquery.com/wrap/)
*
* ## jQuery/jqLite Extras
* Angular also provides the following additional methods and events to both jQuery and jqLite:
*
* ### Events
* - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
* on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
* element before it is removed.
*
* ### Methods
* - `controller(name)` - retrieves the controller of the current element or its parent. By default
* retrieves controller associated with the `ngController` directive. If `name` is provided as
* camelCase directive name, then the controller for this directive will be retrieved (e.g.
* `'ngModel'`).
* - `injector()` - retrieves the injector of the current element or its parent.
* - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
* element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
* be enabled.
* - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
* current element. This getter should be used only on elements that contain a directive which starts a new isolate
* scope. Calling `scope()` on this element always returns the original non-isolate scope.
* Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
* parent element is reached.
*
* @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
* @returns {Object} jQuery object.
*/
JQLite.expando = 'ng339';
var jqCache = JQLite.cache = {},
jqId = 1,
addEventListenerFn = function(element, type, fn) {
element.addEventListener(type, fn, false);
},
removeEventListenerFn = function(element, type, fn) {
element.removeEventListener(type, fn, false);
};
/*
* !!! This is an undocumented "private" function !!!
*/
JQLite._data = function(node) {
//jQuery always returns an object on cache miss
return this.cache[node[this.expando]] || {};
};
function jqNextId() { return ++jqId; }
var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
var MOZ_HACK_REGEXP = /^moz([A-Z])/;
var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
var jqLiteMinErr = minErr('jqLite');
/**
* Converts snake_case to camelCase.
* Also there is special case for Moz prefix starting with upper case letter.
* @param name Name to normalize
*/
function camelCase(name) {
return name.
replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
return offset ? letter.toUpperCase() : letter;
}).
replace(MOZ_HACK_REGEXP, 'Moz$1');
}
var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
var HTML_REGEXP = /<|&#?\w+;/;
var TAG_NAME_REGEXP = /<([\w:]+)/;
var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
var wrapMap = {
'option': [1, '<select multiple="multiple">', '</select>'],
'thead': [1, '<table>', '</table>'],
'col': [2, '<table><colgroup>', '</colgroup></table>'],
'tr': [2, '<table><tbody>', '</tbody></table>'],
'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
'_default': [0, "", ""]
};
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
function jqLiteIsTextNode(html) {
return !HTML_REGEXP.test(html);
}
function jqLiteAcceptsData(node) {
// The window object can accept data but has no nodeType
// Otherwise we are only interested in elements (1) and documents (9)
var nodeType = node.nodeType;
return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
}
function jqLiteBuildFragment(html, context) {
var tmp, tag, wrap,
fragment = context.createDocumentFragment(),
nodes = [], i;
if (jqLiteIsTextNode(html)) {
// Convert non-html into a text node
nodes.push(context.createTextNode(html));
} else {
// Convert html into DOM nodes
tmp = tmp || fragment.appendChild(context.createElement("div"));
tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
wrap = wrapMap[tag] || wrapMap._default;
tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
// Descend through wrappers to the right content
i = wrap[0];
while (i--) {
tmp = tmp.lastChild;
}
nodes = concat(nodes, tmp.childNodes);
tmp = fragment.firstChild;
tmp.textContent = "";
}
// Remove wrapper from fragment
fragment.textContent = "";
fragment.innerHTML = ""; // Clear inner HTML
forEach(nodes, function(node) {
fragment.appendChild(node);
});
return fragment;
}
function jqLiteParseHTML(html, context) {
context = context || document;
var parsed;
if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
return [context.createElement(parsed[1])];
}
if ((parsed = jqLiteBuildFragment(html, context))) {
return parsed.childNodes;
}
return [];
}
/////////////////////////////////////////////
function JQLite(element) {
if (element instanceof JQLite) {
return element;
}
var argIsString;
if (isString(element)) {
element = trim(element);
argIsString = true;
}
if (!(this instanceof JQLite)) {
if (argIsString && element.charAt(0) != '<') {
throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
}
return new JQLite(element);
}
if (argIsString) {
jqLiteAddNodes(this, jqLiteParseHTML(element));
} else {
jqLiteAddNodes(this, element);
}
}
function jqLiteClone(element) {
return element.cloneNode(true);
}
function jqLiteDealoc(element, onlyDescendants) {
if (!onlyDescendants) jqLiteRemoveData(element);
if (element.querySelectorAll) {
var descendants = element.querySelectorAll('*');
for (var i = 0, l = descendants.length; i < l; i++) {
jqLiteRemoveData(descendants[i]);
}
}
}
function jqLiteOff(element, type, fn, unsupported) {
if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
var expandoStore = jqLiteExpandoStore(element);
var events = expandoStore && expandoStore.events;
var handle = expandoStore && expandoStore.handle;
if (!handle) return; //no listeners registered
if (!type) {
for (type in events) {
if (type !== '$destroy') {
removeEventListenerFn(element, type, handle);
}
delete events[type];
}
} else {
forEach(type.split(' '), function(type) {
if (isDefined(fn)) {
var listenerFns = events[type];
arrayRemove(listenerFns || [], fn);
if (listenerFns && listenerFns.length > 0) {
return;
}
}
removeEventListenerFn(element, type, handle);
delete events[type];
});
}
}
function jqLiteRemoveData(element, name) {
var expandoId = element.ng339;
var expandoStore = expandoId && jqCache[expandoId];
if (expandoStore) {
if (name) {
delete expandoStore.data[name];
return;
}
if (expandoStore.handle) {
if (expandoStore.events.$destroy) {
expandoStore.handle({}, '$destroy');
}
jqLiteOff(element);
}
delete jqCache[expandoId];
element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
}
}
function jqLiteExpandoStore(element, createIfNecessary) {
var expandoId = element.ng339,
expandoStore = expandoId && jqCache[expandoId];
if (createIfNecessary && !expandoStore) {
element.ng339 = expandoId = jqNextId();
expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
}
return expandoStore;
}
function jqLiteData(element, key, value) {
if (jqLiteAcceptsData(element)) {
var isSimpleSetter = isDefined(value);
var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
var massGetter = !key;
var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
var data = expandoStore && expandoStore.data;
if (isSimpleSetter) { // data('key', value)
data[key] = value;
} else {
if (massGetter) { // data()
return data;
} else {
if (isSimpleGetter) { // data('key')
// don't force creation of expandoStore if it doesn't exist yet
return data && data[key];
} else { // mass-setter: data({key1: val1, key2: val2})
extend(data, key);
}
}
}
}
}
function jqLiteHasClass(element, selector) {
if (!element.getAttribute) return false;
return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
indexOf(" " + selector + " ") > -1);
}
function jqLiteRemoveClass(element, cssClasses) {
if (cssClasses && element.setAttribute) {
forEach(cssClasses.split(' '), function(cssClass) {
element.setAttribute('class', trim(
(" " + (element.getAttribute('class') || '') + " ")
.replace(/[\n\t]/g, " ")
.replace(" " + trim(cssClass) + " ", " "))
);
});
}
}
function jqLiteAddClass(element, cssClasses) {
if (cssClasses && element.setAttribute) {
var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
.replace(/[\n\t]/g, " ");
forEach(cssClasses.split(' '), function(cssClass) {
cssClass = trim(cssClass);
if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
existingClasses += cssClass + ' ';
}
});
element.setAttribute('class', trim(existingClasses));
}
}
function jqLiteAddNodes(root, elements) {
// THIS CODE IS VERY HOT. Don't make changes without benchmarking.
if (elements) {
// if a Node (the most common case)
if (elements.nodeType) {
root[root.length++] = elements;
} else {
var length = elements.length;
// if an Array or NodeList and not a Window
if (typeof length === 'number' && elements.window !== elements) {
if (length) {
for (var i = 0; i < length; i++) {
root[root.length++] = elements[i];
}
}
} else {
root[root.length++] = elements;
}
}
}
}
function jqLiteController(element, name) {
return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
}
function jqLiteInheritedData(element, name, value) {
// if element is the document object work with the html element instead
// this makes $(document).scope() possible
if (element.nodeType == NODE_TYPE_DOCUMENT) {
element = element.documentElement;
}
var names = isArray(name) ? name : [name];
while (element) {
for (var i = 0, ii = names.length; i < ii; i++) {
if ((value = jqLite.data(element, names[i])) !== undefined) return value;
}
// If dealing with a document fragment node with a host element, and no parent, use the host
// element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
// to lookup parent controllers.
element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
}
}
function jqLiteEmpty(element) {
jqLiteDealoc(element, true);
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
function jqLiteRemove(element, keepData) {
if (!keepData) jqLiteDealoc(element);
var parent = element.parentNode;
if (parent) parent.removeChild(element);
}
function jqLiteDocumentLoaded(action, win) {
win = win || window;
if (win.document.readyState === 'complete') {
// Force the action to be run async for consistent behaviour
// from the action's point of view
// i.e. it will definitely not be in a $apply
win.setTimeout(action);
} else {
// No need to unbind this handler as load is only ever called once
jqLite(win).on('load', action);
}
}
//////////////////////////////////////////
// Functions which are declared directly.
//////////////////////////////////////////
var JQLitePrototype = JQLite.prototype = {
ready: function(fn) {
var fired = false;
function trigger() {
if (fired) return;
fired = true;
fn();
}
// check if document is already loaded
if (document.readyState === 'complete') {
setTimeout(trigger);
} else {
this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
// we can not use jqLite since we are not done loading and jQuery could be loaded later.
// jshint -W064
JQLite(window).on('load', trigger); // fallback to window.onload for others
// jshint +W064
}
},
toString: function() {
var value = [];
forEach(this, function(e) { value.push('' + e);});
return '[' + value.join(', ') + ']';
},
eq: function(index) {
return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
},
length: 0,
push: push,
sort: [].sort,
splice: [].splice
};
//////////////////////////////////////////
// Functions iterating getter/setters.
// these functions return self on setter and
// value on get.
//////////////////////////////////////////
var BOOLEAN_ATTR = {};
forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
BOOLEAN_ATTR[lowercase(value)] = value;
});
var BOOLEAN_ELEMENTS = {};
forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
BOOLEAN_ELEMENTS[value] = true;
});
var ALIASED_ATTR = {
'ngMinlength': 'minlength',
'ngMaxlength': 'maxlength',
'ngMin': 'min',
'ngMax': 'max',
'ngPattern': 'pattern'
};
function getBooleanAttrName(element, name) {
// check dom last since we will most likely fail on name
var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
// booleanAttr is here twice to minimize DOM access
return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
}
function getAliasedAttrName(element, name) {
var nodeName = element.nodeName;
return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
}
forEach({
data: jqLiteData,
removeData: jqLiteRemoveData
}, function(fn, name) {
JQLite[name] = fn;
});
forEach({
data: jqLiteData,
inheritedData: jqLiteInheritedData,
scope: function(element) {
// Can't use jqLiteData here directly so we stay compatible with jQuery!
return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
},
isolateScope: function(element) {
// Can't use jqLiteData here directly so we stay compatible with jQuery!
return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
},
controller: jqLiteController,
injector: function(element) {
return jqLiteInheritedData(element, '$injector');
},
removeAttr: function(element, name) {
element.removeAttribute(name);
},
hasClass: jqLiteHasClass,
css: function(element, name, value) {
name = camelCase(name);
if (isDefined(value)) {
element.style[name] = value;
} else {
return element.style[name];
}
},
attr: function(element, name, value) {
var nodeType = element.nodeType;
if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
return;
}
var lowercasedName = lowercase(name);
if (BOOLEAN_ATTR[lowercasedName]) {
if (isDefined(value)) {
if (!!value) {
element[name] = true;
element.setAttribute(name, lowercasedName);
} else {
element[name] = false;
element.removeAttribute(lowercasedName);
}
} else {
return (element[name] ||
(element.attributes.getNamedItem(name) || noop).specified)
? lowercasedName
: undefined;
}
} else if (isDefined(value)) {
element.setAttribute(name, value);
} else if (element.getAttribute) {
// the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
// some elements (e.g. Document) don't have get attribute, so return undefined
var ret = element.getAttribute(name, 2);
// normalize non-existing attributes to undefined (as jQuery)
return ret === null ? undefined : ret;
}
},
prop: function(element, name, value) {
if (isDefined(value)) {
element[name] = value;
} else {
return element[name];
}
},
text: (function() {
getText.$dv = '';
return getText;
function getText(element, value) {
if (isUndefined(value)) {
var nodeType = element.nodeType;
return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
}
element.textContent = value;
}
})(),
val: function(element, value) {
if (isUndefined(value)) {
if (element.multiple && nodeName_(element) === 'select') {
var result = [];
forEach(element.options, function(option) {
if (option.selected) {
result.push(option.value || option.text);
}
});
return result.length === 0 ? null : result;
}
return element.value;
}
element.value = value;
},
html: function(element, value) {
if (isUndefined(value)) {
return element.innerHTML;
}
jqLiteDealoc(element, true);
element.innerHTML = value;
},
empty: jqLiteEmpty
}, function(fn, name) {
/**
* Properties: writes return selection, reads return first value
*/
JQLite.prototype[name] = function(arg1, arg2) {
var i, key;
var nodeCount = this.length;
// jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
// in a way that survives minification.
// jqLiteEmpty takes no arguments but is a setter.
if (fn !== jqLiteEmpty &&
(((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
if (isObject(arg1)) {
// we are a write, but the object properties are the key/values
for (i = 0; i < nodeCount; i++) {
if (fn === jqLiteData) {
// data() takes the whole object in jQuery
fn(this[i], arg1);
} else {
for (key in arg1) {
fn(this[i], key, arg1[key]);
}
}
}
// return self for chaining
return this;
} else {
// we are a read, so read the first child.
// TODO: do we still need this?
var value = fn.$dv;
// Only if we have $dv do we iterate over all, otherwise it is just the first element.
var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
for (var j = 0; j < jj; j++) {
var nodeValue = fn(this[j], arg1, arg2);
value = value ? value + nodeValue : nodeValue;
}
return value;
}
} else {
// we are a write, so apply to all children
for (i = 0; i < nodeCount; i++) {
fn(this[i], arg1, arg2);
}
// return self for chaining
return this;
}
};
});
function createEventHandler(element, events) {
var eventHandler = function(event, type) {
// jQuery specific api
event.isDefaultPrevented = function() {
return event.defaultPrevented;
};
var eventFns = events[type || event.type];
var eventFnsLength = eventFns ? eventFns.length : 0;
if (!eventFnsLength) return;
if (isUndefined(event.immediatePropagationStopped)) {
var originalStopImmediatePropagation = event.stopImmediatePropagation;
event.stopImmediatePropagation = function() {
event.immediatePropagationStopped = true;
if (event.stopPropagation) {
event.stopPropagation();
}
if (originalStopImmediatePropagation) {
originalStopImmediatePropagation.call(event);
}
};
}
event.isImmediatePropagationStopped = function() {
return event.immediatePropagationStopped === true;
};
// Copy event handlers in case event handlers array is modified during execution.
if ((eventFnsLength > 1)) {
eventFns = shallowCopy(eventFns);
}
for (var i = 0; i < eventFnsLength; i++) {
if (!event.isImmediatePropagationStopped()) {
eventFns[i].call(element, event);
}
}
};
// TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
// events on `element`
eventHandler.elem = element;
return eventHandler;
}
//////////////////////////////////////////
// Functions iterating traversal.
// These functions chain results into a single
// selector.
//////////////////////////////////////////
forEach({
removeData: jqLiteRemoveData,
on: function jqLiteOn(element, type, fn, unsupported) {
if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
// Do not add event handlers to non-elements because they will not be cleaned up.
if (!jqLiteAcceptsData(element)) {
return;
}
var expandoStore = jqLiteExpandoStore(element, true);
var events = expandoStore.events;
var handle = expandoStore.handle;
if (!handle) {
handle = expandoStore.handle = createEventHandler(element, events);
}
// http://jsperf.com/string-indexof-vs-split
var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
var i = types.length;
while (i--) {
type = types[i];
var eventFns = events[type];
if (!eventFns) {
events[type] = [];
if (type === 'mouseenter' || type === 'mouseleave') {
// Refer to jQuery's implementation of mouseenter & mouseleave
// Read about mouseenter and mouseleave:
// http://www.quirksmode.org/js/events_mouse.html#link8
jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
var target = this, related = event.relatedTarget;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if (!related || (related !== target && !target.contains(related))) {
handle(event, type);
}
});
} else {
if (type !== '$destroy') {
addEventListenerFn(element, type, handle);
}
}
eventFns = events[type];
}
eventFns.push(fn);
}
},
off: jqLiteOff,
one: function(element, type, fn) {
element = jqLite(element);
//add the listener twice so that when it is called
//you can remove the original function and still be
//able to call element.off(ev, fn) normally
element.on(type, function onFn() {
element.off(type, fn);
element.off(type, onFn);
});
element.on(type, fn);
},
replaceWith: function(element, replaceNode) {
var index, parent = element.parentNode;
jqLiteDealoc(element);
forEach(new JQLite(replaceNode), function(node) {
if (index) {
parent.insertBefore(node, index.nextSibling);
} else {
parent.replaceChild(node, element);
}
index = node;
});
},
children: function(element) {
var children = [];
forEach(element.childNodes, function(element) {
if (element.nodeType === NODE_TYPE_ELEMENT) {
children.push(element);
}
});
return children;
},
contents: function(element) {
return element.contentDocument || element.childNodes || [];
},
append: function(element, node) {
var nodeType = element.nodeType;
if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
node = new JQLite(node);
for (var i = 0, ii = node.length; i < ii; i++) {
var child = node[i];
element.appendChild(child);
}
},
prepend: function(element, node) {
if (element.nodeType === NODE_TYPE_ELEMENT) {
var index = element.firstChild;
forEach(new JQLite(node), function(child) {
element.insertBefore(child, index);
});
}
},
wrap: function(element, wrapNode) {
wrapNode = jqLite(wrapNode).eq(0).clone()[0];
var parent = element.parentNode;
if (parent) {
parent.replaceChild(wrapNode, element);
}
wrapNode.appendChild(element);
},
remove: jqLiteRemove,
detach: function(element) {
jqLiteRemove(element, true);
},
after: function(element, newElement) {
var index = element, parent = element.parentNode;
newElement = new JQLite(newElement);
for (var i = 0, ii = newElement.length; i < ii; i++) {
var node = newElement[i];
parent.insertBefore(node, index.nextSibling);
index = node;
}
},
addClass: jqLiteAddClass,
removeClass: jqLiteRemoveClass,
toggleClass: function(element, selector, condition) {
if (selector) {
forEach(selector.split(' '), function(className) {
var classCondition = condition;
if (isUndefined(classCondition)) {
classCondition = !jqLiteHasClass(element, className);
}
(classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
});
}
},
parent: function(element) {
var parent = element.parentNode;
return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
},
next: function(element) {
return element.nextElementSibling;
},
find: function(element, selector) {
if (element.getElementsByTagName) {
return element.getElementsByTagName(selector);
} else {
return [];
}
},
clone: jqLiteClone,
triggerHandler: function(element, event, extraParameters) {
var dummyEvent, eventFnsCopy, handlerArgs;
var eventName = event.type || event;
var expandoStore = jqLiteExpandoStore(element);
var events = expandoStore && expandoStore.events;
var eventFns = events && events[eventName];
if (eventFns) {
// Create a dummy event to pass to the handlers
dummyEvent = {
preventDefault: function() { this.defaultPrevented = true; },
isDefaultPrevented: function() { return this.defaultPrevented === true; },
stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
stopPropagation: noop,
type: eventName,
target: element
};
// If a custom event was provided then extend our dummy event with it
if (event.type) {
dummyEvent = extend(dummyEvent, event);
}
// Copy event handlers in case event handlers array is modified during execution.
eventFnsCopy = shallowCopy(eventFns);
handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
forEach(eventFnsCopy, function(fn) {
if (!dummyEvent.isImmediatePropagationStopped()) {
fn.apply(element, handlerArgs);
}
});
}
}
}, function(fn, name) {
/**
* chaining functions
*/
JQLite.prototype[name] = function(arg1, arg2, arg3) {
var value;
for (var i = 0, ii = this.length; i < ii; i++) {
if (isUndefined(value)) {
value = fn(this[i], arg1, arg2, arg3);
if (isDefined(value)) {
// any function which returns a value needs to be wrapped
value = jqLite(value);
}
} else {
jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
}
}
return isDefined(value) ? value : this;
};
// bind legacy bind/unbind to on/off
JQLite.prototype.bind = JQLite.prototype.on;
JQLite.prototype.unbind = JQLite.prototype.off;
});
// Provider for private $$jqLite service
function $$jqLiteProvider() {
this.$get = function $$jqLite() {
return extend(JQLite, {
hasClass: function(node, classes) {
if (node.attr) node = node[0];
return jqLiteHasClass(node, classes);
},
addClass: function(node, classes) {
if (node.attr) node = node[0];
return jqLiteAddClass(node, classes);
},
removeClass: function(node, classes) {
if (node.attr) node = node[0];
return jqLiteRemoveClass(node, classes);
}
});
};
}
/**
* Computes a hash of an 'obj'.
* Hash of a:
* string is string
* number is number as string
* object is either result of calling $$hashKey function on the object or uniquely generated id,
* that is also assigned to the $$hashKey property of the object.
*
* @param obj
* @returns {string} hash string such that the same input will have the same hash string.
* The resulting string key is in 'type:hashKey' format.
*/
function hashKey(obj, nextUidFn) {
var key = obj && obj.$$hashKey;
if (key) {
if (typeof key === 'function') {
key = obj.$$hashKey();
}
return key;
}
var objType = typeof obj;
if (objType == 'function' || (objType == 'object' && obj !== null)) {
key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
} else {
key = objType + ':' + obj;
}
return key;
}
/**
* HashMap which can use objects as keys
*/
function HashMap(array, isolatedUid) {
if (isolatedUid) {
var uid = 0;
this.nextUid = function() {
return ++uid;
};
}
forEach(array, this.put, this);
}
HashMap.prototype = {
/**
* Store key value pair
* @param key key to store can be any type
* @param value value to store can be any type
*/
put: function(key, value) {
this[hashKey(key, this.nextUid)] = value;
},
/**
* @param key
* @returns {Object} the value for the key
*/
get: function(key) {
return this[hashKey(key, this.nextUid)];
},
/**
* Remove the key/value pair
* @param key
*/
remove: function(key) {
var value = this[key = hashKey(key, this.nextUid)];
delete this[key];
return value;
}
};
var $$HashMapProvider = [function() {
this.$get = [function() {
return HashMap;
}];
}];
/**
* @ngdoc function
* @module ng
* @name angular.injector
* @kind function
*
* @description
* Creates an injector object that can be used for retrieving services as well as for
* dependency injection (see {@link guide/di dependency injection}).
*
* @param {Array.<string|Function>} modules A list of module functions or their aliases. See
* {@link angular.module}. The `ng` module must be explicitly added.
* @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
* disallows argument name annotation inference.
* @returns {injector} Injector object. See {@link auto.$injector $injector}.
*
* @example
* Typical usage
* ```js
* // create an injector
* var $injector = angular.injector(['ng']);
*
* // use the injector to kick off your application
* // use the type inference to auto inject arguments, or use implicit injection
* $injector.invoke(function($rootScope, $compile, $document) {
* $compile($document)($rootScope);
* $rootScope.$digest();
* });
* ```
*
* Sometimes you want to get access to the injector of a currently running Angular app
* from outside Angular. Perhaps, you want to inject and compile some markup after the
* application has been bootstrapped. You can do this using the extra `injector()` added
* to JQuery/jqLite elements. See {@link angular.element}.
*
* *This is fairly rare but could be the case if a third party library is injecting the
* markup.*
*
* In the following example a new block of HTML containing a `ng-controller`
* directive is added to the end of the document body by JQuery. We then compile and link
* it into the current AngularJS scope.
*
* ```js
* var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
* $(document.body).append($div);
*
* angular.element(document).injector().invoke(function($compile) {
* var scope = angular.element($div).scope();
* $compile($div)(scope);
* });
* ```
*/
/**
* @ngdoc module
* @name auto
* @description
*
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
*/
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');
function anonFn(fn) {
// For anonymous functions, showing at the very least the function signature can help in
// debugging.
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
args = fnText.match(FN_ARGS);
if (args) {
return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
}
return 'fn';
}
function annotate(fn, strictDi, name) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn === 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
if (fn.length) {
if (strictDi) {
if (!isString(name) || !name) {
name = fn.name || anonFn(fn);
}
throw $injectorMinErr('strictdi',
'{0} is not using explicit annotation and cannot be invoked in strict mode', name);
}
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
arg.replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
});
});
}
fn.$inject = $inject;
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
}
return $inject;
}
///////////////////////////////////////
/**
* @ngdoc service
* @name $injector
*
* @description
*
* `$injector` is used to retrieve object instances as defined by
* {@link auto.$provide provider}, instantiate types, invoke methods,
* and load modules.
*
* The following always holds true:
*
* ```js
* var $injector = angular.injector();
* expect($injector.get('$injector')).toBe($injector);
* expect($injector.invoke(function($injector) {
* return $injector;
* })).toBe($injector);
* ```
*
* # Injection Function Annotation
*
* JavaScript does not have annotations, and annotations are needed for dependency injection. The
* following are all valid ways of annotating function with injection arguments and are equivalent.
*
* ```js
* // inferred (only works if code not minified/obfuscated)
* $injector.invoke(function(serviceA){});
*
* // annotated
* function explicit(serviceA) {};
* explicit.$inject = ['serviceA'];
* $injector.invoke(explicit);
*
* // inline
* $injector.invoke(['serviceA', function(serviceA){}]);
* ```
*
* ## Inference
*
* In JavaScript calling `toString()` on a function returns the function definition. The definition
* can then be parsed and the function arguments can be extracted. This method of discovering
* annotations is disallowed when the injector is in strict mode.
* *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
* argument names.
*
* ## `$inject` Annotation
* By adding an `$inject` property onto a function the injection parameters can be specified.
*
* ## Inline
* As an array of injection names, where the last item in the array is the function to call.
*/
/**
* @ngdoc method
* @name $injector#get
*
* @description
* Return an instance of the service.
*
* @param {string} name The name of the instance to retrieve.
* @param {string=} caller An optional string to provide the origin of the function call for error messages.
* @return {*} The instance.
*/
/**
* @ngdoc method
* @name $injector#invoke
*
* @description
* Invoke the method and supply the method arguments from the `$injector`.
*
* @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
* injected according to the {@link guide/di $inject Annotation} rules.
* @param {Object=} self The `this` for the invoked method.
* @param {Object=} locals Optional object. If preset then any argument names are read from this
* object first, before the `$injector` is consulted.
* @returns {*} the value returned by the invoked `fn` function.
*/
/**
* @ngdoc method
* @name $injector#has
*
* @description
* Allows the user to query if the particular service exists.
*
* @param {string} name Name of the service to query.
* @returns {boolean} `true` if injector has given service.
*/
/**
* @ngdoc method
* @name $injector#instantiate
* @description
* Create a new instance of JS type. The method takes a constructor function, invokes the new
* operator, and supplies all of the arguments to the constructor function as specified by the
* constructor annotation.
*
* @param {Function} Type Annotated constructor function.
* @param {Object=} locals Optional object. If preset then any argument names are read from this
* object first, before the `$injector` is consulted.
* @returns {Object} new instance of `Type`.
*/
/**
* @ngdoc method
* @name $injector#annotate
*
* @description
* Returns an array of service names which the function is requesting for injection. This API is
* used by the injector to determine which services need to be injected into the function when the
* function is invoked. There are three ways in which the function can be annotated with the needed
* dependencies.
*
* # Argument names
*
* The simplest form is to extract the dependencies from the arguments of the function. This is done
* by converting the function into a string using `toString()` method and extracting the argument
* names.
* ```js
* // Given
* function MyController($scope, $route) {
* // ...
* }
*
* // Then
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
* ```
*
* You can disallow this method by using strict injection mode.
*
* This method does not work with code minification / obfuscation. For this reason the following
* annotation strategies are supported.
*
* # The `$inject` property
*
* If a function has an `$inject` property and its value is an array of strings, then the strings
* represent names of services to be injected into the function.
* ```js
* // Given
* var MyController = function(obfuscatedScope, obfuscatedRoute) {
* // ...
* }
* // Define function dependencies
* MyController['$inject'] = ['$scope', '$route'];
*
* // Then
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
* ```
*
* # The array notation
*
* It is often desirable to inline Injected functions and that's when setting the `$inject` property
* is very inconvenient. In these situations using the array notation to specify the dependencies in
* a way that survives minification is a better choice:
*
* ```js
* // We wish to write this (not minification / obfuscation safe)
* injector.invoke(function($compile, $rootScope) {
* // ...
* });
*
* // We are forced to write break inlining
* var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
* // ...
* };
* tmpFn.$inject = ['$compile', '$rootScope'];
* injector.invoke(tmpFn);
*
* // To better support inline function the inline annotation is supported
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
* // ...
* }]);
*
* // Therefore
* expect(injector.annotate(
* ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
* ).toEqual(['$compile', '$rootScope']);
* ```
*
* @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
* be retrieved as described above.
*
* @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
*
* @returns {Array.<string>} The names of the services which the function requires.
*/
/**
* @ngdoc service
* @name $provide
*
* @description
*
* The {@link auto.$provide $provide} service has a number of methods for registering components
* with the {@link auto.$injector $injector}. Many of these functions are also exposed on
* {@link angular.Module}.
*
* An Angular **service** is a singleton object created by a **service factory**. These **service
* factories** are functions which, in turn, are created by a **service provider**.
* The **service providers** are constructor functions. When instantiated they must contain a
* property called `$get`, which holds the **service factory** function.
*
* When you request a service, the {@link auto.$injector $injector} is responsible for finding the
* correct **service provider**, instantiating it and then calling its `$get` **service factory**
* function to get the instance of the **service**.
*
* Often services have no configuration options and there is no need to add methods to the service
* provider. The provider will be no more than a constructor function with a `$get` property. For
* these cases the {@link auto.$provide $provide} service has additional helper methods to register
* services without specifying a provider.
*
* * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
* {@link auto.$injector $injector}
* * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
* providers and services.
* * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
* services, not providers.
* * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
* that will be wrapped in a **service provider** object, whose `$get` property will contain the
* given factory function.
* * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
* that will be wrapped in a **service provider** object, whose `$get` property will instantiate
* a new object using the given constructor function.
*
* See the individual methods for more information and examples.
*/
/**
* @ngdoc method
* @name $provide#provider
* @description
*
* Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
* are constructor functions, whose instances are responsible for "providing" a factory for a
* service.
*
* Service provider names start with the name of the service they provide followed by `Provider`.
* For example, the {@link ng.$log $log} service has a provider called
* {@link ng.$logProvider $logProvider}.
*
* Service provider objects can have additional methods which allow configuration of the provider
* and its service. Importantly, you can configure what kind of service is created by the `$get`
* method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
* method {@link ng.$logProvider#debugEnabled debugEnabled}
* which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
* console or not.
*
* @param {string} name The name of the instance. NOTE: the provider will be available under `name +
'Provider'` key.
* @param {(Object|function())} provider If the provider is:
*
* - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
* {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
* - `Constructor`: a new instance of the provider will be created using
* {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
*
* @returns {Object} registered provider instance
* @example
*
* The following example shows how to create a simple event tracking service and register it using
* {@link auto.$provide#provider $provide.provider()}.
*
* ```js
* // Define the eventTracker provider
* function EventTrackerProvider() {
* var trackingUrl = '/track';
*
* // A provider method for configuring where the tracked events should been saved
* this.setTrackingUrl = function(url) {
* trackingUrl = url;
* };
*
* // The service factory function
* this.$get = ['$http', function($http) {
* var trackedEvents = {};
* return {
* // Call this to track an event
* event: function(event) {
* var count = trackedEvents[event] || 0;
* count += 1;
* trackedEvents[event] = count;
* return count;
* },
* // Call this to save the tracked events to the trackingUrl
* save: function() {
* $http.post(trackingUrl, trackedEvents);
* }
* };
* }];
* }
*
* describe('eventTracker', function() {
* var postSpy;
*
* beforeEach(module(function($provide) {
* // Register the eventTracker provider
* $provide.provider('eventTracker', EventTrackerProvider);
* }));
*
* beforeEach(module(function(eventTrackerProvider) {
* // Configure eventTracker provider
* eventTrackerProvider.setTrackingUrl('/custom-track');
* }));
*
* it('tracks events', inject(function(eventTracker) {
* expect(eventTracker.event('login')).toEqual(1);
* expect(eventTracker.event('login')).toEqual(2);
* }));
*
* it('saves to the tracking url', inject(function(eventTracker, $http) {
* postSpy = spyOn($http, 'post');
* eventTracker.event('login');
* eventTracker.save();
* expect(postSpy).toHaveBeenCalled();
* expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
* expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
* expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
* }));
* });
* ```
*/
/**
* @ngdoc method
* @name $provide#factory
* @description
*
* Register a **service factory**, which will be called to return the service instance.
* This is short for registering a service where its provider consists of only a `$get` property,
* which is the given service factory function.
* You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
* configure your service in a provider.
*
* @param {string} name The name of the instance.
* @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
* Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
* @returns {Object} registered provider instance
*
* @example
* Here is an example of registering a service
* ```js
* $provide.factory('ping', ['$http', function($http) {
* return function ping() {
* return $http.send('/ping');
* };
* }]);
* ```
* You would then inject and use this service like this:
* ```js
* someModule.controller('Ctrl', ['ping', function(ping) {
* ping();
* }]);
* ```
*/
/**
* @ngdoc method
* @name $provide#service
* @description
*
* Register a **service constructor**, which will be invoked with `new` to create the service
* instance.
* This is short for registering a service where its provider's `$get` property is the service
* constructor function that will be used to instantiate the service instance.
*
* You should use {@link auto.$provide#service $provide.service(class)} if you define your service
* as a type/class.
*
* @param {string} name The name of the instance.
* @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
* that will be instantiated.
* @returns {Object} registered provider instance
*
* @example
* Here is an example of registering a service using
* {@link auto.$provide#service $provide.service(class)}.
* ```js
* var Ping = function($http) {
* this.$http = $http;
* };
*
* Ping.$inject = ['$http'];
*
* Ping.prototype.send = function() {
* return this.$http.get('/ping');
* };
* $provide.service('ping', Ping);
* ```
* You would then inject and use this service like this:
* ```js
* someModule.controller('Ctrl', ['ping', function(ping) {
* ping.send();
* }]);
* ```
*/
/**
* @ngdoc method
* @name $provide#value
* @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
* provider's `$get` property is a factory function that takes no arguments and returns the **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}.
*
* @param {string} name The name of the instance.
* @param {*} value The value.
* @returns {Object} registered provider instance
*
* @example
* Here are some examples of creating value services.
* ```js
* $provide.value('ADMIN_USER', 'admin');
*
* $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
*
* $provide.value('halfOf', function(value) {
* return value / 2;
* });
* ```
*/
/**
* @ngdoc method
* @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
* injected into a module configuration function (see {@link angular.Module#config}) and it cannot
* be overridden by an Angular {@link auto.$provide#decorator decorator}.
*
* @param {string} name The name of the constant.
* @param {*} value The constant value.
* @returns {Object} registered instance
*
* @example
* Here a some examples of creating constants:
* ```js
* $provide.constant('SHARD_HEIGHT', 306);
*
* $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
*
* $provide.constant('double', function(value) {
* return value * 2;
* });
* ```
*/
/**
* @ngdoc method
* @name $provide#decorator
* @description
*
* Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
* intercepts the creation of a service, allowing it to override or modify the behaviour of the
* service. The object returned by the decorator may be the original service, or a new service
* object which replaces or wraps and delegates to the original service.
*
* @param {string} name The name of the service to decorate.
* @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
* instantiated and should return the decorated service instance. The function is called using
* the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
* Local injection arguments:
*
* * `$delegate` - The original service instance, which can be monkey patched, configured,
* decorated or delegated to.
*
* @example
* Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
* calls to {@link ng.$log#error $log.warn()}.
* ```js
* $provide.decorator('$log', ['$delegate', function($delegate) {
* $delegate.warn = $delegate.error;
* return $delegate;
* }]);
* ```
*/
function createInjector(modulesToLoad, strictDi) {
strictDi = (strictDi === true);
var INSTANTIATING = {},
providerSuffix = 'Provider',
path = [],
loadedModules = new HashMap([], true),
providerCache = {
$provide: {
provider: supportObject(provider),
factory: supportObject(factory),
service: supportObject(service),
value: supportObject(value),
constant: supportObject(constant),
decorator: decorator
}
},
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function(serviceName, caller) {
if (angular.isString(caller)) {
path.push(caller);
}
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
})),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(serviceName, caller) {
var provider = providerInjector.get(serviceName + providerSuffix, caller);
return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
}));
forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
return instanceInjector;
////////////////////////////////////
// $provider
////////////////////////////////////
function supportObject(delegate) {
return function(key, value) {
if (isObject(key)) {
forEach(key, reverseParams(delegate));
} else {
return delegate(key, value);
}
};
}
function provider(name, provider_) {
assertNotHasOwnProperty(name, 'service');
if (isFunction(provider_) || isArray(provider_)) {
provider_ = providerInjector.instantiate(provider_);
}
if (!provider_.$get) {
throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
}
return providerCache[name + providerSuffix] = provider_;
}
function enforceReturnValue(name, factory) {
return function enforcedReturnValue() {
var result = instanceInjector.invoke(factory, this);
if (isUndefined(result)) {
throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
}
return result;
};
}
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
}
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
function value(name, val) { return factory(name, valueFn(val), false); }
function constant(name, value) {
assertNotHasOwnProperty(name, 'constant');
providerCache[name] = value;
instanceCache[name] = value;
}
function decorator(serviceName, decorFn) {
var origProvider = providerInjector.get(serviceName + providerSuffix),
orig$get = origProvider.$get;
origProvider.$get = function() {
var origInstance = instanceInjector.invoke(orig$get, origProvider);
return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
};
}
////////////////////////////////////
// Module Loading
////////////////////////////////////
function loadModules(modulesToLoad) {
var runBlocks = [], moduleFn;
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return;
loadedModules.put(module, true);
function runInvokeQueue(queue) {
var i, ii;
for (i = 0, ii = queue.length; i < ii; i++) {
var invokeArgs = queue[i],
provider = providerInjector.get(invokeArgs[0]);
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
}
}
try {
if (isString(module)) {
moduleFn = angularModule(module);
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
runInvokeQueue(moduleFn._invokeQueue);
runInvokeQueue(moduleFn._configBlocks);
} else if (isFunction(module)) {
runBlocks.push(providerInjector.invoke(module));
} else if (isArray(module)) {
runBlocks.push(providerInjector.invoke(module));
} else {
assertArgFn(module, 'module');
}
} catch (e) {
if (isArray(module)) {
module = module[module.length - 1];
}
if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
// Safari & FF's stack traces don't contain error.message content
// unlike those of Chrome and IE
// So if stack doesn't contain message, we create a new string that contains both.
// Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
/* jshint -W022 */
e = e.message + '\n' + e.stack;
}
throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
module, e.stack || e.message || e);
}
});
return runBlocks;
}
////////////////////////////////////
// internal Injector
////////////////////////////////////
function createInternalInjector(cache, factory) {
function getService(serviceName, caller) {
if (cache.hasOwnProperty(serviceName)) {
if (cache[serviceName] === INSTANTIATING) {
throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
serviceName + ' <- ' + path.join(' <- '));
}
return cache[serviceName];
} else {
try {
path.unshift(serviceName);
cache[serviceName] = INSTANTIATING;
return cache[serviceName] = factory(serviceName, caller);
} catch (err) {
if (cache[serviceName] === INSTANTIATING) {
delete cache[serviceName];
}
throw err;
} finally {
path.shift();
}
}
}
function invoke(fn, self, locals, serviceName) {
if (typeof locals === 'string') {
serviceName = locals;
locals = null;
}
var args = [],
$inject = createInjector.$$annotate(fn, strictDi, serviceName),
length, i,
key;
for (i = 0, length = $inject.length; i < length; i++) {
key = $inject[i];
if (typeof key !== 'string') {
throw $injectorMinErr('itkn',
'Incorrect injection token! Expected service name as string, got {0}', key);
}
args.push(
locals && locals.hasOwnProperty(key)
? locals[key]
: getService(key, serviceName)
);
}
if (isArray(fn)) {
fn = fn[length];
}
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self, args);
}
function instantiate(Type, locals, serviceName) {
// Check if Type is annotated and use just the given function at n-1 as parameter
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
// Object creation: http://jsperf.com/create-constructor/2
var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
var returnedValue = invoke(Type, instance, locals, serviceName);
return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
}
return {
invoke: invoke,
instantiate: instantiate,
get: getService,
annotate: createInjector.$$annotate,
has: function(name) {
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
}
};
}
}
createInjector.$$annotate = annotate;
/**
* @ngdoc provider
* @name $anchorScrollProvider
*
* @description
* Use `$anchorScrollProvider` to disable automatic scrolling whenever
* {@link ng.$location#hash $location.hash()} changes.
*/
function $AnchorScrollProvider() {
var autoScrollingEnabled = true;
/**
* @ngdoc method
* @name $anchorScrollProvider#disableAutoScrolling
*
* @description
* By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
* {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
* Use this method to disable automatic scrolling.
*
* If automatic scrolling is disabled, one must explicitly call
* {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
* current hash.
*/
this.disableAutoScrolling = function() {
autoScrollingEnabled = false;
};
/**
* @ngdoc service
* @name $anchorScroll
* @kind function
* @requires $window
* @requires $location
* @requires $rootScope
*
* @description
* When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
* current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
* in the
* [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
*
* It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
* match any anchor whenever it changes. This can be disabled by calling
* {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
*
* Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
* vertical scroll-offset (either fixed or dynamic).
*
* @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
* {@link ng.$location#hash $location.hash()} will be used.
*
* @property {(number|function|jqLite)} yOffset
* If set, specifies a vertical scroll-offset. This is often useful when there are fixed
* positioned elements at the top of the page, such as navbars, headers etc.
*
* `yOffset` can be specified in various ways:
* - **number**: A fixed number of pixels to be used as offset.<br /><br />
* - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
* a number representing the offset (in pixels).<br /><br />
* - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
* the top of the page to the element's bottom will be used as offset.<br />
* **Note**: The element will be taken into account only as long as its `position` is set to
* `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
* their height and/or positioning according to the viewport's size.
*
* <br />
* <div class="alert alert-warning">
* In order for `yOffset` to work properly, scrolling should take place on the document's root and
* not some child element.
* </div>
*
* @example
<example module="anchorScrollExample">
<file name="index.html">
<div id="scrollArea" ng-controller="ScrollController">
<a ng-click="gotoBottom()">Go to bottom</a>
<a id="bottom"></a> You're at the bottom!
</div>
</file>
<file name="script.js">
angular.module('anchorScrollExample', [])
.controller('ScrollController', ['$scope', '$location', '$anchorScroll',
function ($scope, $location, $anchorScroll) {
$scope.gotoBottom = function() {
// set the location.hash to the id of
// the element you wish to scroll to.
$location.hash('bottom');
// call $anchorScroll()
$anchorScroll();
};
}]);
</file>
<file name="style.css">
#scrollArea {
height: 280px;
overflow: auto;
}
#bottom {
display: block;
margin-top: 2000px;
}
</file>
</example>
*
* <hr />
* The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
* See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
*
* @example
<example module="anchorScrollOffsetExample">
<file name="index.html">
<div class="fixed-header" ng-controller="headerCtrl">
<a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
Go to anchor {{x}}
</a>
</div>
<div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
Anchor {{x}} of 5
</div>
</file>
<file name="script.js">
angular.module('anchorScrollOffsetExample', [])
.run(['$anchorScroll', function($anchorScroll) {
$anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
}])
.controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
function ($anchorScroll, $location, $scope) {
$scope.gotoAnchor = function(x) {
var newHash = 'anchor' + x;
if ($location.hash() !== newHash) {
// set the $location.hash to `newHash` and
// $anchorScroll will automatically scroll to it
$location.hash('anchor' + x);
} else {
// call $anchorScroll() explicitly,
// since $location.hash hasn't changed
$anchorScroll();
}
};
}
]);
</file>
<file name="style.css">
body {
padding-top: 50px;
}
.anchor {
border: 2px dashed DarkOrchid;
padding: 10px 10px 200px 10px;
}
.fixed-header {
background-color: rgba(0, 0, 0, 0.2);
height: 50px;
position: fixed;
top: 0; left: 0; right: 0;
}
.fixed-header > a {
display: inline-block;
margin: 5px 15px;
}
</file>
</example>
*/
this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
var document = $window.document;
// Helper function to get first anchor from a NodeList
// (using `Array#some()` instead of `angular#forEach()` since it's more performant
// and working in all supported browsers.)
function getFirstAnchor(list) {
var result = null;
Array.prototype.some.call(list, function(element) {
if (nodeName_(element) === 'a') {
result = element;
return true;
}
});
return result;
}
function getYOffset() {
var offset = scroll.yOffset;
if (isFunction(offset)) {
offset = offset();
} else if (isElement(offset)) {
var elem = offset[0];
var style = $window.getComputedStyle(elem);
if (style.position !== 'fixed') {
offset = 0;
} else {
offset = elem.getBoundingClientRect().bottom;
}
} else if (!isNumber(offset)) {
offset = 0;
}
return offset;
}
function scrollTo(elem) {
if (elem) {
elem.scrollIntoView();
var offset = getYOffset();
if (offset) {
// `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
// This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
// top of the viewport.
//
// IF the number of pixels from the top of `elem` to the end of the page's content is less
// than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
// way down the page.
//
// This is often the case for elements near the bottom of the page.
//
// In such cases we do not need to scroll the whole `offset` up, just the difference between
// the top of the element and the offset, which is enough to align the top of `elem` at the
// desired position.
var elemTop = elem.getBoundingClientRect().top;
$window.scrollBy(0, elemTop - offset);
}
} else {
$window.scrollTo(0, 0);
}
}
function scroll(hash) {
hash = isString(hash) ? hash : $location.hash();
var elm;
// empty hash, scroll to the top of the page
if (!hash) scrollTo(null);
// element with given id
else if ((elm = document.getElementById(hash))) scrollTo(elm);
// first anchor with given name :-D
else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
// no element and hash == 'top', scroll to the top of the page
else if (hash === 'top') scrollTo(null);
}
// does not scroll when user clicks on anchor link that is currently on
// (no url change, no $location.hash() change), browser native does scroll
if (autoScrollingEnabled) {
$rootScope.$watch(function autoScrollWatch() {return $location.hash();},
function autoScrollWatchAction(newVal, oldVal) {
// skip the initial scroll if $location.hash is empty
if (newVal === oldVal && newVal === '') return;
jqLiteDocumentLoaded(function() {
$rootScope.$evalAsync(scroll);
});
});
}
return scroll;
}];
}
var $animateMinErr = minErr('$animate');
var ELEMENT_NODE = 1;
var NG_ANIMATE_CLASSNAME = 'ng-animate';
function mergeClasses(a,b) {
if (!a && !b) return '';
if (!a) return b;
if (!b) return a;
if (isArray(a)) a = a.join(' ');
if (isArray(b)) b = b.join(' ');
return a + ' ' + b;
}
function extractElementNode(element) {
for (var i = 0; i < element.length; i++) {
var elm = element[i];
if (elm.nodeType === ELEMENT_NODE) {
return elm;
}
}
}
function splitClasses(classes) {
if (isString(classes)) {
classes = classes.split(' ');
}
// Use createMap() to prevent class assumptions involving property names in
// Object.prototype
var obj = createMap();
forEach(classes, function(klass) {
// sometimes the split leaves empty string values
// incase extra spaces were applied to the options
if (klass.length) {
obj[klass] = true;
}
});
return obj;
}
// if any other type of options value besides an Object value is
// passed into the $animate.method() animation then this helper code
// will be run which will ignore it. While this patch is not the
// greatest solution to this, a lot of existing plugins depend on
// $animate to either call the callback (< 1.2) or return a promise
// that can be changed. This helper function ensures that the options
// are wiped clean incase a callback function is provided.
function prepareAnimateOptions(options) {
return isObject(options)
? options
: {};
}
var $$CoreAnimateRunnerProvider = function() {
this.$get = ['$q', '$$rAF', function($q, $$rAF) {
function AnimateRunner() {}
AnimateRunner.all = noop;
AnimateRunner.chain = noop;
AnimateRunner.prototype = {
end: noop,
cancel: noop,
resume: noop,
pause: noop,
complete: noop,
then: function(pass, fail) {
return $q(function(resolve) {
$$rAF(function() {
resolve();
});
}).then(pass, fail);
}
};
return AnimateRunner;
}];
};
// this is prefixed with Core since it conflicts with
// the animateQueueProvider defined in ngAnimate/animateQueue.js
var $$CoreAnimateQueueProvider = function() {
var postDigestQueue = new HashMap();
var postDigestElements = [];
this.$get = ['$$AnimateRunner', '$rootScope',
function($$AnimateRunner, $rootScope) {
return {
enabled: noop,
on: noop,
off: noop,
pin: noop,
push: function(element, event, options, domOperation) {
domOperation && domOperation();
options = options || {};
options.from && element.css(options.from);
options.to && element.css(options.to);
if (options.addClass || options.removeClass) {
addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
}
return new $$AnimateRunner(); // jshint ignore:line
}
};
function addRemoveClassesPostDigest(element, add, remove) {
var data = postDigestQueue.get(element);
var classVal;
if (!data) {
postDigestQueue.put(element, data = {});
postDigestElements.push(element);
}
if (add) {
forEach(add.split(' '), function(className) {
if (className) {
data[className] = true;
}
});
}
if (remove) {
forEach(remove.split(' '), function(className) {
if (className) {
data[className] = false;
}
});
}
if (postDigestElements.length > 1) return;
$rootScope.$$postDigest(function() {
forEach(postDigestElements, function(element) {
var data = postDigestQueue.get(element);
if (data) {
var existing = splitClasses(element.attr('class'));
var toAdd = '';
var toRemove = '';
forEach(data, function(status, className) {
var hasClass = !!existing[className];
if (status !== hasClass) {
if (status) {
toAdd += (toAdd.length ? ' ' : '') + className;
} else {
toRemove += (toRemove.length ? ' ' : '') + className;
}
}
});
forEach(element, function(elm) {
toAdd && jqLiteAddClass(elm, toAdd);
toRemove && jqLiteRemoveClass(elm, toRemove);
});
postDigestQueue.remove(element);
}
});
postDigestElements.length = 0;
});
}
}];
};
/**
* @ngdoc provider
* @name $animateProvider
*
* @description
* Default implementation of $animate that doesn't perform any animations, instead just
* synchronously performs DOM updates and resolves the returned runner promise.
*
* In order to enable animations the `ngAnimate` module has to be loaded.
*
* To see the functional implementation check out `src/ngAnimate/animate.js`.
*/
var $AnimateProvider = ['$provide', function($provide) {
var provider = this;
this.$$registeredAnimations = Object.create(null);
/**
* @ngdoc method
* @name $animateProvider#register
*
* @description
* Registers a new injectable animation factory function. The factory function produces the
* animation object which contains callback functions for each event that is expected to be
* animated.
*
* * `eventFn`: `function(element, ... , doneFunction, options)`
* The element to animate, the `doneFunction` and the options fed into the animation. Depending
* on the type of animation additional arguments will be injected into the animation function. The
* list below explains the function signatures for the different animation methods:
*
* - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
* - addClass: function(element, addedClasses, doneFunction, options)
* - removeClass: function(element, removedClasses, doneFunction, options)
* - enter, leave, move: function(element, doneFunction, options)
* - animate: function(element, fromStyles, toStyles, doneFunction, options)
*
* Make sure to trigger the `doneFunction` once the animation is fully complete.
*
* ```js
* return {
* //enter, leave, move signature
* eventFn : function(element, done, options) {
* //code to run the animation
* //once complete, then run done()
* return function endFunction(wasCancelled) {
* //code to cancel the animation
* }
* }
* }
* ```
*
* @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
* @param {Function} factory The factory function that will be executed to return the animation
* object.
*/
this.register = function(name, factory) {
if (name && name.charAt(0) !== '.') {
throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
}
var key = name + '-animation';
provider.$$registeredAnimations[name.substr(1)] = key;
$provide.factory(key, factory);
};
/**
* @ngdoc method
* @name $animateProvider#classNameFilter
*
* @description
* Sets and/or returns the CSS class regular expression that is checked when performing
* an animation. Upon bootstrap the classNameFilter value is not set at all and will
* therefore enable $animate to attempt to perform an animation on any element that is triggered.
* When setting the `classNameFilter` value, animations will only be performed on elements
* that successfully match the filter expression. This in turn can boost performance
* for low-powered devices as well as applications containing a lot of structural operations.
* @param {RegExp=} expression The className expression which will be checked against all animations
* @return {RegExp} The current CSS className expression value. If null then there is no expression value
*/
this.classNameFilter = function(expression) {
if (arguments.length === 1) {
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
if (this.$$classNameFilter) {
var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
if (reservedRegex.test(this.$$classNameFilter.toString())) {
throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
}
}
}
return this.$$classNameFilter;
};
this.$get = ['$$animateQueue', function($$animateQueue) {
function domInsert(element, parentElement, afterElement) {
// if for some reason the previous element was removed
// from the dom sometime before this code runs then let's
// just stick to using the parent element as the anchor
if (afterElement) {
var afterNode = extractElementNode(afterElement);
if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
afterElement = null;
}
}
afterElement ? afterElement.after(element) : parentElement.prepend(element);
}
/**
* @ngdoc service
* @name $animate
* @description The $animate service exposes a series of DOM utility methods that provide support
* for animation hooks. The default behavior is the application of DOM operations, however,
* when an animation is detected (and animations are enabled), $animate will do the heavy lifting
* to ensure that animation runs with the triggered DOM operation.
*
* By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't
* included and only when it is active then the animation hooks that `$animate` triggers will be
* functional. Once active then all structural `ng-` directives will trigger animations as they perform
* their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
* `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
*
* It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
*
* To learn more about enabling animation support, click here to visit the
* {@link ngAnimate ngAnimate module page}.
*/
return {
// we don't call it directly since non-existant arguments may
// be interpreted as null within the sub enabled function
/**
*
* @ngdoc method
* @name $animate#on
* @kind function
* @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
* has fired on the given element or among any of its children. Once the listener is fired, the provided callback
* is fired with the following params:
*
* ```js
* $animate.on('enter', container,
* function callback(element, phase) {
* // cool we detected an enter animation within the container
* }
* );
* ```
*
* @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
* @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
* as well as among its children
* @param {Function} callback the callback function that will be fired when the listener is triggered
*
* The arguments present in the callback function are:
* * `element` - The captured DOM element that the animation was fired on.
* * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
*/
on: $$animateQueue.on,
/**
*
* @ngdoc method
* @name $animate#off
* @kind function
* @description Deregisters an event listener based on the event which has been associated with the provided element. This method
* can be used in three different ways depending on the arguments:
*
* ```js
* // remove all the animation event listeners listening for `enter`
* $animate.off('enter');
*
* // remove all the animation event listeners listening for `enter` on the given element and its children
* $animate.off('enter', container);
*
* // remove the event listener function provided by `listenerFn` that is set
* // to listen for `enter` on the given `element` as well as its children
* $animate.off('enter', container, callback);
* ```
*
* @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
* @param {DOMElement=} container the container element the event listener was placed on
* @param {Function=} callback the callback function that was registered as the listener
*/
off: $$animateQueue.off,
/**
* @ngdoc method
* @name $animate#pin
* @kind function
* @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
* outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
* element despite being outside the realm of the application or within another application. Say for example if the application
* was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
* as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
* that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
*
* Note that this feature is only active when the `ngAnimate` module is used.
*
* @param {DOMElement} element the external element that will be pinned
* @param {DOMElement} parentElement the host parent element that will be associated with the external element
*/
pin: $$animateQueue.pin,
/**
*
* @ngdoc method
* @name $animate#enabled
* @kind function
* @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
* function can be called in four ways:
*
* ```js
* // returns true or false
* $animate.enabled();
*
* // changes the enabled state for all animations
* $animate.enabled(false);
* $animate.enabled(true);
*
* // returns true or false if animations are enabled for an element
* $animate.enabled(element);
*
* // changes the enabled state for an element and its children
* $animate.enabled(element, true);
* $animate.enabled(element, false);
* ```
*
* @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
* @param {boolean=} enabled whether or not the animations will be enabled for the element
*
* @return {boolean} whether or not animations are enabled
*/
enabled: $$animateQueue.enabled,
/**
* @ngdoc method
* @name $animate#cancel
* @kind function
* @description Cancels the provided animation.
*
* @param {Promise} animationPromise The animation promise that is returned when an animation is started.
*/
cancel: function(runner) {
runner.end && runner.end();
},
/**
*
* @ngdoc method
* @name $animate#enter
* @kind function
* @description Inserts the element into the DOM either after the `after` element (if provided) or
* as the first child within the `parent` element and then triggers an animation.
* A promise is returned that will be resolved during the next digest once the animation
* has completed.
*
* @param {DOMElement} element the element which will be inserted into the DOM
* @param {DOMElement} parent the parent element which will append the element as
* a child (so long as the after element is not present)
* @param {DOMElement=} after the sibling element after which the element will be appended
* @param {object=} options an optional collection of options/styles that will be applied to the element
*
* @return {Promise} the animation callback promise
*/
enter: function(element, parent, after, options) {
parent = parent && jqLite(parent);
after = after && jqLite(after);
parent = parent || after.parent();
domInsert(element, parent, after);
return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
},
/**
*
* @ngdoc method
* @name $animate#move
* @kind function
* @description Inserts (moves) the element into its new position in the DOM either after
* the `after` element (if provided) or as the first child within the `parent` element
* and then triggers an animation. A promise is returned that will be resolved
* during the next digest once the animation has completed.
*
* @param {DOMElement} element the element which will be moved into the new DOM position
* @param {DOMElement} parent the parent element which will append the element as
* a child (so long as the after element is not present)
* @param {DOMElement=} after the sibling element after which the element will be appended
* @param {object=} options an optional collection of options/styles that will be applied to the element
*
* @return {Promise} the animation callback promise
*/
move: function(element, parent, after, options) {
parent = parent && jqLite(parent);
after = after && jqLite(after);
parent = parent || after.parent();
domInsert(element, parent, after);
return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
},
/**
* @ngdoc method
* @name $animate#leave
* @kind function
* @description Triggers an animation and then removes the element from the DOM.
* When the function is called a promise is returned that will be resolved during the next
* digest once the animation has completed.
*
* @param {DOMElement} element the element which will be removed from the DOM
* @param {object=} options an optional collection of options/styles that will be applied to the element
*
* @return {Promise} the animation callback promise
*/
leave: function(element, options) {
return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
element.remove();
});
},
/**
* @ngdoc method
* @name $animate#addClass
* @kind function
*
* @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
* execution, the addClass operation will only be handled after the next digest and it will not trigger an
* animation if element already contains the CSS class or if the class is removed at a later step.
* Note that class-based animations are treated differently compared to structural animations
* (like enter, move and leave) since the CSS classes may be added/removed at different points
* depending if CSS or JavaScript animations are used.
*
* @param {DOMElement} element the element which the CSS classes will be applied to
* @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
* @param {object=} options an optional collection of options/styles that will be applied to the element
*
* @return {Promise} the animation callback promise
*/
addClass: function(element, className, options) {
options = prepareAnimateOptions(options);
options.addClass = mergeClasses(options.addclass, className);
return $$animateQueue.push(element, 'addClass', options);
},
/**
* @ngdoc method
* @name $animate#removeClass
* @kind function
*
* @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
* execution, the removeClass operation will only be handled after the next digest and it will not trigger an
* animation if element does not contain the CSS class or if the class is added at a later step.
* Note that class-based animations are treated differently compared to structural animations
* (like enter, move and leave) since the CSS classes may be added/removed at different points
* depending if CSS or JavaScript animations are used.
*
* @param {DOMElement} element the element which the CSS classes will be applied to
* @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
* @param {object=} options an optional collection of options/styles that will be applied to the element
*
* @return {Promise} the animation callback promise
*/
removeClass: function(element, className, options) {
options = prepareAnimateOptions(options);
options.removeClass = mergeClasses(options.removeClass, className);
return $$animateQueue.push(element, 'removeClass', options);
},
/**
* @ngdoc method
* @name $animate#setClass
* @kind function
*
* @description Performs both the addition and removal of a CSS classes on an element and (during the process)
* triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
* `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
* passed. Note that class-based animations are treated differently compared to structural animations
* (like enter, move and leave) since the CSS classes may be added/removed at different points
* depending if CSS or JavaScript animations are used.
*
* @param {DOMElement} element the element which the CSS classes will be applied to
* @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
* @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
* @param {object=} options an optional collection of options/styles that will be applied to the element
*
* @return {Promise} the animation callback promise
*/
setClass: function(element, add, remove, options) {
options = prepareAnimateOptions(options);
options.addClass = mergeClasses(options.addClass, add);
options.removeClass = mergeClasses(options.removeClass, remove);
return $$animateQueue.push(element, 'setClass', options);
},
/**
* @ngdoc method
* @name $animate#animate
* @kind function
*
* @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
* If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
* on the provided styles. For example, if a transition animation is set for the given className then the provided from and
* to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
* will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
*
* @param {DOMElement} element the element which the CSS styles will be applied to
* @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
* @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
* @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
* this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
* (Note that if no animation is detected then this value will not be appplied to the element.)
* @param {object=} options an optional collection of options/styles that will be applied to the element
*
* @return {Promise} the animation callback promise
*/
animate: function(element, from, to, className, options) {
options = prepareAnimateOptions(options);
options.from = options.from ? extend(options.from, from) : from;
options.to = options.to ? extend(options.to, to) : to;
className = className || 'ng-inline-animate';
options.tempClasses = mergeClasses(options.tempClasses, className);
return $$animateQueue.push(element, 'animate', options);
}
};
}];
}];
function $$AsyncCallbackProvider() {
this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
return $$rAF.supported
? function(fn) { return $$rAF(fn); }
: function(fn) {
return $timeout(fn, 0, false);
};
}];
}
/* global stripHash: true */
/**
* ! This is a private undocumented service !
*
* @name $browser
* @requires $log
* @description
* This object has two goals:
*
* - hide all the global state in the browser caused by the window object
* - abstract away all the browser specific features and inconsistencies
*
* For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
* service, which can be used for convenient testing of the application without the interaction with
* the real browser apis.
*/
/**
* @param {object} window The global window object.
* @param {object} document jQuery wrapped document.
* @param {object} $log window.console or an object with the same interface.
* @param {object} $sniffer $sniffer service
*/
function Browser(window, document, $log, $sniffer) {
var self = this,
rawDocument = document[0],
location = window.location,
history = window.history,
setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
pendingDeferIds = {};
self.isMock = false;
var outstandingRequestCount = 0;
var outstandingRequestCallbacks = [];
// TODO(vojta): remove this temporary api
self.$$completeOutstandingRequest = completeOutstandingRequest;
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
/**
* Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
* counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
*/
function completeOutstandingRequest(fn) {
try {
fn.apply(null, sliceArgs(arguments, 1));
} finally {
outstandingRequestCount--;
if (outstandingRequestCount === 0) {
while (outstandingRequestCallbacks.length) {
try {
outstandingRequestCallbacks.pop()();
} catch (e) {
$log.error(e);
}
}
}
}
}
function getHash(url) {
var index = url.indexOf('#');
return index === -1 ? '' : url.substr(index + 1);
}
/**
* @private
* Note: this method is used only by scenario runner
* TODO(vojta): prefix this method with $$ ?
* @param {function()} callback Function that will be called when no outstanding request
*/
self.notifyWhenNoOutstandingRequests = function(callback) {
if (outstandingRequestCount === 0) {
callback();
} else {
outstandingRequestCallbacks.push(callback);
}
};
//////////////////////////////////////////////////////////////
// URL API
//////////////////////////////////////////////////////////////
var cachedState, lastHistoryState,
lastBrowserUrl = location.href,
baseElement = document.find('base'),
reloadLocation = null;
cacheState();
lastHistoryState = cachedState;
/**
* @name $browser#url
*
* @description
* GETTER:
* Without any argument, this method just returns current value of location.href.
*
* SETTER:
* With at least one argument, this method sets url to new value.
* If html5 history api supported, pushState/replaceState is used, otherwise
* location.href/location.replace is used.
* Returns its own instance to allow chaining
*
* NOTE: this api is intended for use only by the $location service. Please use the
* {@link ng.$location $location service} to change url.
*
* @param {string} url New url (when used as setter)
* @param {boolean=} replace Should new url replace current history record?
* @param {object=} state object to use with pushState/replaceState
*/
self.url = function(url, replace, state) {
// In modern browsers `history.state` is `null` by default; treating it separately
// from `undefined` would cause `$browser.url('/foo')` to change `history.state`
// to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
if (isUndefined(state)) {
state = null;
}
// Android Browser BFCache causes location, history reference to become stale.
if (location !== window.location) location = window.location;
if (history !== window.history) history = window.history;
// setter
if (url) {
var sameState = lastHistoryState === state;
// Don't change anything if previous and current URLs and states match. This also prevents
// IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
// See https://github.com/angular/angular.js/commit/ffb2701
if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
return self;
}
var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
lastBrowserUrl = url;
lastHistoryState = state;
// Don't use history API if only the hash changed
// due to a bug in IE10/IE11 which leads
// to not firing a `hashchange` nor `popstate` event
// in some cases (see #9143).
if ($sniffer.history && (!sameBase || !sameState)) {
history[replace ? 'replaceState' : 'pushState'](state, '', url);
cacheState();
// Do the assignment again so that those two variables are referentially identical.
lastHistoryState = cachedState;
} else {
if (!sameBase) {
reloadLocation = url;
}
if (replace) {
location.replace(url);
} else if (!sameBase) {
location.href = url;
} else {
location.hash = getHash(url);
}
}
return self;
// getter
} else {
// - reloadLocation is needed as browsers don't allow to read out
// the new location.href if a reload happened.
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
return reloadLocation || location.href.replace(/%27/g,"'");
}
};
/**
* @name $browser#state
*
* @description
* This method is a getter.
*
* Return history.state or null if history.state is undefined.
*
* @returns {object} state
*/
self.state = function() {
return cachedState;
};
var urlChangeListeners = [],
urlChangeInit = false;
function cacheStateAndFireUrlChange() {
cacheState();
fireUrlChange();
}
function getCurrentState() {
try {
return history.state;
} catch (e) {
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
}
}
// This variable should be used *only* inside the cacheState function.
var lastCachedState = null;
function cacheState() {
// This should be the only place in $browser where `history.state` is read.
cachedState = getCurrentState();
cachedState = isUndefined(cachedState) ? null : cachedState;
// Prevent callbacks fo fire twice if both hashchange & popstate were fired.
if (equals(cachedState, lastCachedState)) {
cachedState = lastCachedState;
}
lastCachedState = cachedState;
}
function fireUrlChange() {
if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
return;
}
lastBrowserUrl = self.url();
lastHistoryState = cachedState;
forEach(urlChangeListeners, function(listener) {
listener(self.url(), cachedState);
});
}
/**
* @name $browser#onUrlChange
*
* @description
* Register callback function that will be called, when url changes.
*
* It's only called when the url is changed from outside of angular:
* - user types different url into address bar
* - user clicks on history (forward/back) button
* - user clicks on a link
*
* It's not called when url is changed by $browser.url() method
*
* The listener gets called with new url as parameter.
*
* NOTE: this api is intended for use only by the $location service. Please use the
* {@link ng.$location $location service} to monitor url changes in angular apps.
*
* @param {function(string)} listener Listener function to be called when url changes.
* @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
*/
self.onUrlChange = function(callback) {
// TODO(vojta): refactor to use node's syntax for events
if (!urlChangeInit) {
// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
// don't fire popstate when user change the address bar and don't fire hashchange when url
// changed by push/replaceState
// html5 history api - popstate event
if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
// hashchange event
jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
urlChangeInit = true;
}
urlChangeListeners.push(callback);
return callback;
};
/**
* @private
* Remove popstate and hashchange handler from window.
*
* NOTE: this api is intended for use only by $rootScope.
*/
self.$$applicationDestroyed = function() {
jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
};
/**
* Checks whether the url has changed outside of Angular.
* Needs to be exported to be able to check for changes that have been done in sync,
* as hashchange/popstate events fire in async.
*/
self.$$checkUrlChange = fireUrlChange;
//////////////////////////////////////////////////////////////
// Misc API
//////////////////////////////////////////////////////////////
/**
* @name $browser#baseHref
*
* @description
* Returns current <base href>
* (always relative - without domain)
*
* @returns {string} The current base href
*/
self.baseHref = function() {
var href = baseElement.attr('href');
return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
};
/**
* @name $browser#defer
* @param {function()} fn A function, who's execution should be deferred.
* @param {number=} [delay=0] of milliseconds to defer the function execution.
* @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
*
* @description
* Executes a fn asynchronously via `setTimeout(fn, delay)`.
*
* Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
* `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
* via `$browser.defer.flush()`.
*
*/
self.defer = function(fn, delay) {
var timeoutId;
outstandingRequestCount++;
timeoutId = setTimeout(function() {
delete pendingDeferIds[timeoutId];
completeOutstandingRequest(fn);
}, delay || 0);
pendingDeferIds[timeoutId] = true;
return timeoutId;
};
/**
* @name $browser#defer.cancel
*
* @description
* Cancels a deferred task identified with `deferId`.
*
* @param {*} deferId Token returned by the `$browser.defer` function.
* @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
* canceled.
*/
self.defer.cancel = function(deferId) {
if (pendingDeferIds[deferId]) {
delete pendingDeferIds[deferId];
clearTimeout(deferId);
completeOutstandingRequest(noop);
return true;
}
return false;
};
}
function $BrowserProvider() {
this.$get = ['$window', '$log', '$sniffer', '$document',
function($window, $log, $sniffer, $document) {
return new Browser($window, $document, $log, $sniffer);
}];
}
/**
* @ngdoc service
* @name $cacheFactory
*
* @description
* Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
* them.
*
* ```js
*
* var cache = $cacheFactory('cacheId');
* expect($cacheFactory.get('cacheId')).toBe(cache);
* expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
*
* cache.put("key", "value");
* cache.put("another key", "another value");
*
* // We've specified no options on creation
* expect(cache.info()).toEqual({id: 'cacheId', size: 2});
*
* ```
*
*
* @param {string} cacheId Name or id of the newly created cache.
* @param {object=} options Options object that specifies the cache behavior. Properties:
*
* - `{number=}` `capacity` — turns the cache into LRU cache.
*
* @returns {object} Newly created cache object with the following set of methods:
*
* - `{object}` `info()` — Returns id, size, and options of cache.
* - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
* it.
* - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
* - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
* - `{void}` `removeAll()` — Removes all cached values.
* - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
*
* @example
<example module="cacheExampleApp">
<file name="index.html">
<div ng-controller="CacheController">
<input ng-model="newCacheKey" placeholder="Key">
<input ng-model="newCacheValue" placeholder="Value">
<button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
<p ng-if="keys.length">Cached Values</p>
<div ng-repeat="key in keys">
<span ng-bind="key"></span>
<span>: </span>
<b ng-bind="cache.get(key)"></b>
</div>
<p>Cache Info</p>
<div ng-repeat="(key, value) in cache.info()">
<span ng-bind="key"></span>
<span>: </span>
<b ng-bind="value"></b>
</div>
</div>
</file>
<file name="script.js">
angular.module('cacheExampleApp', []).
controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
$scope.keys = [];
$scope.cache = $cacheFactory('cacheId');
$scope.put = function(key, value) {
if ($scope.cache.get(key) === undefined) {
$scope.keys.push(key);
}
$scope.cache.put(key, value === undefined ? null : value);
};
}]);
</file>
<file name="style.css">
p {
margin: 10px 0 3px;
}
</file>
</example>
*/
function $CacheFactoryProvider() {
this.$get = function() {
var caches = {};
function cacheFactory(cacheId, options) {
if (cacheId in caches) {
throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
}
var size = 0,
stats = extend({}, options, {id: cacheId}),
data = {},
capacity = (options && options.capacity) || Number.MAX_VALUE,
lruHash = {},
freshEnd = null,
staleEnd = null;
/**
* @ngdoc type
* @name $cacheFactory.Cache
*
* @description
* A cache object used to store and retrieve data, primarily used by
* {@link $http $http} and the {@link ng.directive:script script} directive to cache
* templates and other data.
*
* ```js
* angular.module('superCache')
* .factory('superCache', ['$cacheFactory', function($cacheFactory) {
* return $cacheFactory('super-cache');
* }]);
* ```
*
* Example test:
*
* ```js
* it('should behave like a cache', inject(function(superCache) {
* superCache.put('key', 'value');
* superCache.put('another key', 'another value');
*
* expect(superCache.info()).toEqual({
* id: 'super-cache',
* size: 2
* });
*
* superCache.remove('another key');
* expect(superCache.get('another key')).toBeUndefined();
*
* superCache.removeAll();
* expect(superCache.info()).toEqual({
* id: 'super-cache',
* size: 0
* });
* }));
* ```
*/
return caches[cacheId] = {
/**
* @ngdoc method
* @name $cacheFactory.Cache#put
* @kind function
*
* @description
* Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
* retrieved later, and incrementing the size of the cache if the key was not already
* present in the cache. If behaving like an LRU cache, it will also remove stale
* entries from the set.
*
* It will not insert undefined values into the cache.
*
* @param {string} key the key under which the cached data is stored.
* @param {*} value the value to store alongside the key. If it is undefined, the key
* will not be stored.
* @returns {*} the value stored.
*/
put: function(key, value) {
if (isUndefined(value)) return;
if (capacity < Number.MAX_VALUE) {
var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
refresh(lruEntry);
}
if (!(key in data)) size++;
data[key] = value;
if (size > capacity) {
this.remove(staleEnd.key);
}
return value;
},
/**
* @ngdoc method
* @name $cacheFactory.Cache#get
* @kind function
*
* @description
* Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
*
* @param {string} key the key of the data to be retrieved
* @returns {*} the value stored.
*/
get: function(key) {
if (capacity < Number.MAX_VALUE) {
var lruEntry = lruHash[key];
if (!lruEntry) return;
refresh(lruEntry);
}
return data[key];
},
/**
* @ngdoc method
* @name $cacheFactory.Cache#remove
* @kind function
*
* @description
* Removes an entry from the {@link $cacheFactory.Cache Cache} object.
*
* @param {string} key the key of the entry to be removed
*/
remove: function(key) {
if (capacity < Number.MAX_VALUE) {
var lruEntry = lruHash[key];
if (!lruEntry) return;
if (lruEntry == freshEnd) freshEnd = lruEntry.p;
if (lruEntry == staleEnd) staleEnd = lruEntry.n;
link(lruEntry.n,lruEntry.p);
delete lruHash[key];
}
delete data[key];
size--;
},
/**
* @ngdoc method
* @name $cacheFactory.Cache#removeAll
* @kind function
*
* @description
* Clears the cache object of any entries.
*/
removeAll: function() {
data = {};
size = 0;
lruHash = {};
freshEnd = staleEnd = null;
},
/**
* @ngdoc method
* @name $cacheFactory.Cache#destroy
* @kind function
*
* @description
* Destroys the {@link $cacheFactory.Cache Cache} object entirely,
* removing it from the {@link $cacheFactory $cacheFactory} set.
*/
destroy: function() {
data = null;
stats = null;
lruHash = null;
delete caches[cacheId];
},
/**
* @ngdoc method
* @name $cacheFactory.Cache#info
* @kind function
*
* @description
* Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
*
* @returns {object} an object with the following properties:
* <ul>
* <li>**id**: the id of the cache instance</li>
* <li>**size**: the number of entries kept in the cache instance</li>
* <li>**...**: any additional properties from the options object when creating the
* cache.</li>
* </ul>
*/
info: function() {
return extend({}, stats, {size: size});
}
};
/**
* makes the `entry` the freshEnd of the LRU linked list
*/
function refresh(entry) {
if (entry != freshEnd) {
if (!staleEnd) {
staleEnd = entry;
} else if (staleEnd == entry) {
staleEnd = entry.n;
}
link(entry.n, entry.p);
link(entry, freshEnd);
freshEnd = entry;
freshEnd.n = null;
}
}
/**
* bidirectionally links two entries of the LRU linked list
*/
function link(nextEntry, prevEntry) {
if (nextEntry != prevEntry) {
if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
}
}
}
/**
* @ngdoc method
* @name $cacheFactory#info
*
* @description
* Get information about all the caches that have been created
*
* @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
*/
cacheFactory.info = function() {
var info = {};
forEach(caches, function(cache, cacheId) {
info[cacheId] = cache.info();
});
return info;
};
/**
* @ngdoc method
* @name $cacheFactory#get
*
* @description
* Get access to a cache object by the `cacheId` used when it was created.
*
* @param {string} cacheId Name or id of a cache to access.
* @returns {object} Cache object identified by the cacheId or undefined if no such cache.
*/
cacheFactory.get = function(cacheId) {
return caches[cacheId];
};
return cacheFactory;
};
}
/**
* @ngdoc service
* @name $templateCache
*
* @description
* The first time a template is used, it is loaded in the template cache for quick retrieval. You
* can load templates directly into the cache in a `script` tag, or by consuming the
* `$templateCache` service directly.
*
* Adding via the `script` tag:
*
* ```html
* <script type="text/ng-template" id="templateId.html">
* <p>This is the content of the template</p>
* </script>
* ```
*
* **Note:** the `script` tag containing the template does not need to be included in the `head` of
* the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
* element with ng-app attribute), otherwise the template will be ignored.
*
* Adding via the `$templateCache` service:
*
* ```js
* var myApp = angular.module('myApp', []);
* myApp.run(function($templateCache) {
* $templateCache.put('templateId.html', 'This is the content of the template');
* });
* ```
*
* To retrieve the template later, simply use it in your HTML:
* ```html
* <div ng-include=" 'templateId.html' "></div>
* ```
*
* or get it via Javascript:
* ```js
* $templateCache.get('templateId.html')
* ```
*
* See {@link ng.$cacheFactory $cacheFactory}.
*
*/
function $TemplateCacheProvider() {
this.$get = ['$cacheFactory', function($cacheFactory) {
return $cacheFactory('templates');
}];
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Any commits to this file should be reviewed with security in mind. *
* Changes to this file can potentially create security vulnerabilities. *
* An approval from 2 Core members with history of modifying *
* this file is required. *
* *
* Does the change somehow allow for arbitrary javascript to be executed? *
* Or allows for someone to change the prototype of built-in objects? *
* Or gives undesired access to variables likes document or window? *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
*
* DOM-related variables:
*
* - "node" - DOM Node
* - "element" - DOM Element or Node
* - "$node" or "$element" - jqLite-wrapped node or element
*
*
* Compiler related stuff:
*
* - "linkFn" - linking fn of a single directive
* - "nodeLinkFn" - function that aggregates all linking fns for a particular node
* - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
* - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
*/
/**
* @ngdoc service
* @name $compile
* @kind function
*
* @description
* Compiles an HTML string or DOM into a template and produces a template function, which
* can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
*
* The compilation is a process of walking the DOM tree and matching DOM elements to
* {@link ng.$compileProvider#directive directives}.
*
* <div class="alert alert-warning">
* **Note:** This document is an in-depth reference of all directive options.
* For a gentle introduction to directives with examples of common use cases,
* see the {@link guide/directive directive guide}.
* </div>
*
* ## Comprehensive Directive API
*
* There are many different options for a directive.
*
* The difference resides in the return value of the factory function.
* You can either return a "Directive Definition Object" (see below) that defines the directive properties,
* or just the `postLink` function (all other properties will have the default values).
*
* <div class="alert alert-success">
* **Best Practice:** It's recommended to use the "directive definition object" form.
* </div>
*
* Here's an example directive declared with a Directive Definition Object:
*
* ```js
* var myModule = angular.module(...);
*
* myModule.directive('directiveName', function factory(injectables) {
* var directiveDefinitionObject = {
* priority: 0,
* template: '<div></div>', // or // function(tElement, tAttrs) { ... },
* // or
* // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
* transclude: false,
* restrict: 'A',
* templateNamespace: 'html',
* scope: false,
* controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
* controllerAs: 'stringIdentifier',
* bindToController: false,
* require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
* compile: function compile(tElement, tAttrs, transclude) {
* return {
* pre: function preLink(scope, iElement, iAttrs, controller) { ... },
* post: function postLink(scope, iElement, iAttrs, controller) { ... }
* }
* // or
* // return function postLink( ... ) { ... }
* },
* // or
* // link: {
* // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
* // post: function postLink(scope, iElement, iAttrs, controller) { ... }
* // }
* // or
* // link: function postLink( ... ) { ... }
* };
* return directiveDefinitionObject;
* });
* ```
*
* <div class="alert alert-warning">
* **Note:** Any unspecified options will use the default value. You can see the default values below.
* </div>
*
* Therefore the above can be simplified as:
*
* ```js
* var myModule = angular.module(...);
*
* myModule.directive('directiveName', function factory(injectables) {
* var directiveDefinitionObject = {
* link: function postLink(scope, iElement, iAttrs) { ... }
* };
* return directiveDefinitionObject;
* // or
* // return function postLink(scope, iElement, iAttrs) { ... }
* });
* ```
*
*
*
* ### Directive Definition Object
*
* The directive definition object provides instructions to the {@link ng.$compile
* compiler}. The attributes are:
*
* #### `multiElement`
* When this property is set to true, the HTML compiler will collect DOM nodes between
* nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
* together as the directive elements. It is recommended that this feature be used on directives
* which are not strictly behavioural (such as {@link ngClick}), and which
* do not manipulate or replace child nodes (such as {@link ngInclude}).
*
* #### `priority`
* When there are multiple directives defined on a single DOM element, sometimes it
* is necessary to specify the order in which the directives are applied. The `priority` is used
* to sort the directives before their `compile` functions get called. Priority is defined as a
* number. Directives with greater numerical `priority` are compiled first. Pre-link functions
* are also run in priority order, but post-link functions are run in reverse order. The order
* of directives with the same priority is undefined. The default priority is `0`.
*
* #### `terminal`
* If set to true then the current `priority` will be the last set of directives
* which will execute (any directives at the current priority will still execute
* as the order of execution on same `priority` is undefined). Note that expressions
* and other directives used in the directive's template will also be excluded from execution.
*
* #### `scope`
* **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
* same element request a new scope, only one new scope is created. The new scope rule does not
* apply for the root of the template since the root of the template always gets a new scope.
*
* **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
* normal scope in that it does not prototypically inherit from the parent scope. This is useful
* when creating reusable components, which should not accidentally read or modify data in the
* parent scope.
*
* The 'isolate' scope takes an object hash which defines a set of local scope properties
* derived from the parent scope. These local properties are useful for aliasing values for
* templates. Locals definition is a hash of local scope property to its source:
*
* * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
* always a string since DOM attributes are strings. If no `attr` name is specified then the
* attribute name is assumed to be the same as the local name.
* Given `<widget my-attr="hello {{name}}">` and widget definition
* of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
* the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
* `localName` property on the widget scope. The `name` is read from the parent scope (not
* component scope).
*
* * `=` or `=attr` - set up bi-directional binding between a local scope property and the
* parent scope property of name defined via the value of the `attr` attribute. If no `attr`
* name is specified then the attribute name is assumed to be the same as the local name.
* Given `<widget my-attr="parentModel">` and widget definition of
* `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
* value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
* in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
* scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
* can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
* you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
* `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
*
* * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
* If no `attr` name is specified then the attribute name is assumed to be the same as the
* local name. Given `<widget my-attr="count = count + value">` and widget definition of
* `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
* a function wrapper for the `count = count + value` expression. Often it's desirable to
* pass data from the isolated scope via an expression to the parent scope, this can be
* done by passing a map of local variable names and values into the expression wrapper fn.
* For example, if the expression is `increment(amount)` then we can specify the amount value
* by calling the `localFn` as `localFn({amount: 22})`.
*
*
* #### `bindToController`
* When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
* allow a component to have its properties bound to the controller, rather than to scope. When the controller
* is instantiated, the initial values of the isolate scope bindings are already available.
*
* #### `controller`
* Controller constructor function. The controller is instantiated before the
* pre-linking phase and it is shared with other directives (see
* `require` attribute). This allows the directives to communicate with each other and augment
* each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
*
* * `$scope` - Current scope associated with the element
* * `$element` - Current element
* * `$attrs` - Current attributes object for the element
* * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
* `function([scope], cloneLinkingFn, futureParentElement)`.
* * `scope`: optional argument to override the scope.
* * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
* * `futureParentElement`:
* * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
* * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
* * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
* and when the `cloneLinkinFn` is passed,
* as those elements need to created and cloned in a special way when they are defined outside their
* usual containers (e.g. like `<svg>`).
* * See also the `directive.templateNamespace` property.
*
*
* #### `require`
* Require another directive and inject its controller as the fourth argument to the linking function. The
* `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
* injected argument will be an array in corresponding order. If no such directive can be
* found, or if the directive does not have a controller, then an error is raised (unless no link function
* is specified, in which case error checking is skipped). The name can be prefixed with:
*
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
* * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
* * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
* * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
* `null` to the `link` fn if not found.
* * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
* `null` to the `link` fn if not found.
*
*
* #### `controllerAs`
* Identifier name for a reference to the controller in the directive's scope.
* This allows the controller to be referenced from the directive template. The directive
* needs to define a scope for this configuration to be used. Useful in the case when
* directive is used as component.
*
*
* #### `restrict`
* String of subset of `EACM` which restricts the directive to a specific directive
* declaration style. If omitted, the defaults (elements and attributes) are used.
*
* * `E` - Element name (default): `<my-directive></my-directive>`
* * `A` - Attribute (default): `<div my-directive="exp"></div>`
* * `C` - Class: `<div class="my-directive: exp;"></div>`
* * `M` - Comment: `<!-- directive: my-directive exp -->`
*
*
* #### `templateNamespace`
* String representing the document type used by the markup in the template.
* AngularJS needs this information as those elements need to be created and cloned
* in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
*
* * `html` - All root nodes in the template are HTML. Root nodes may also be
* top-level elements such as `<svg>` or `<math>`.
* * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
* * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
*
* If no `templateNamespace` is specified, then the namespace is considered to be `html`.
*
* #### `template`
* HTML markup that may:
* * Replace the contents of the directive's element (default).
* * Replace the directive's element itself (if `replace` is true - DEPRECATED).
* * Wrap the contents of the directive's element (if `transclude` is true).
*
* Value may be:
*
* * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
* * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
* function api below) and returns a string value.
*
*
* #### `templateUrl`
* This is similar to `template` but the template is loaded from the specified URL, asynchronously.
*
* Because template loading is asynchronous the compiler will suspend compilation of directives on that element
* for later when the template has been resolved. In the meantime it will continue to compile and link
* sibling and parent elements as though this element had not contained any directives.
*
* The compiler does not suspend the entire compilation to wait for templates to be loaded because this
* would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
* case when only one deeply nested directive has `templateUrl`.
*
* Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
*
* You can specify `templateUrl` as a string representing the URL or as a function which takes two
* arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
* a string value representing the url. In either case, the template URL is passed through {@link
* $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
*
*
* #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
* specify what the template should replace. Defaults to `false`.
*
* * `true` - the template will replace the directive's element.
* * `false` - the template will replace the contents of the directive's element.
*
* The replacement process migrates all of the attributes / classes from the old element to the new
* one. See the {@link guide/directive#template-expanding-directive
* Directives Guide} for an example.
*
* There are very few scenarios where element replacement is required for the application function,
* the main one being reusable custom components that are used within SVG contexts
* (because SVG doesn't work with custom elements in the DOM tree).
*
* #### `transclude`
* Extract the contents of the element where the directive appears and make it available to the directive.
* The contents are compiled and provided to the directive as a **transclusion function**. See the
* {@link $compile#transclusion Transclusion} section below.
*
* There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
* directive's element or the entire element:
*
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
* * `'element'` - transclude the whole of the directive's element including any directives on this
* element that defined at a lower priority than this directive. When used, the `template`
* property is ignored.
*
*
* #### `compile`
*
* ```js
* function compile(tElement, tAttrs, transclude) { ... }
* ```
*
* The compile function deals with transforming the template DOM. Since most directives do not do
* template transformation, it is not used often. The compile function takes the following arguments:
*
* * `tElement` - template element - The element where the directive has been declared. It is
* safe to do template transformation on the element and child elements only.
*
* * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
* between all directive compile functions.
*
* * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
*
* <div class="alert alert-warning">
* **Note:** The template instance and the link instance may be different objects if the template has
* been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
* apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
* should be done in a linking function rather than in a compile function.
* </div>
* <div class="alert alert-warning">
* **Note:** The compile function cannot handle directives that recursively use themselves in their
* own templates or compile functions. Compiling these directives results in an infinite loop and a
* stack overflow errors.
*
* This can be avoided by manually using $compile in the postLink function to imperatively compile
* a directive's template instead of relying on automatic template compilation via `template` or
* `templateUrl` declaration or manual compilation inside the compile function.
* </div>
*
* <div class="alert alert-danger">
* **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
* e.g. does not know about the right outer scope. Please use the transclude function that is passed
* to the link function instead.
* </div>
* A compile function can have a return value which can be either a function or an object.
*
* * returning a (post-link) function - is equivalent to registering the linking function via the
* `link` property of the config object when the compile function is empty.
*
* * returning an object with function(s) registered via `pre` and `post` properties - allows you to
* control when a linking function should be called during the linking phase. See info about
* pre-linking and post-linking functions below.
*
*
* #### `link`
* This property is used only if the `compile` property is not defined.
*
* ```js
* function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
* ```
*
* The link function is responsible for registering DOM listeners as well as updating the DOM. It is
* executed after the template has been cloned. This is where most of the directive logic will be
* put.
*
* * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
* directive for registering {@link ng.$rootScope.Scope#$watch watches}.
*
* * `iElement` - instance element - The element where the directive is to be used. It is safe to
* manipulate the children of the element only in `postLink` function since the children have
* already been linked.
*
* * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
* between all directive linking functions.
*
* * `controller` - the directive's required controller instance(s) - Instances are shared
* among all directives, which allows the directives to use the controllers as a communication
* channel. The exact value depends on the directive's `require` property:
* * `string`: the controller instance
* * `array`: array of controller instances
* * no controller(s) required: `undefined`
*
* If a required controller cannot be found, and it is optional, the instance is `null`,
* otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
*
* * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
* This is the same as the `$transclude`
* parameter of directive controllers, see there for details.
* `function([scope], cloneLinkingFn, futureParentElement)`.
*
* #### Pre-linking function
*
* Executed before the child elements are linked. Not safe to do DOM transformation since the
* compiler linking function will fail to locate the correct elements for linking.
*
* #### Post-linking function
*
* Executed after the child elements are linked.
*
* Note that child elements that contain `templateUrl` directives will not have been compiled
* and linked since they are waiting for their template to load asynchronously and their own
* compilation and linking has been suspended until that occurs.
*
* It is safe to do DOM transformation in the post-linking function on elements that are not waiting
* for their async templates to be resolved.
*
*
* ### Transclusion
*
* Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
* copying them to another part of the DOM, while maintaining their connection to the original AngularJS
* scope from where they were taken.
*
* Transclusion is used (often with {@link ngTransclude}) to insert the
* original contents of a directive's element into a specified place in the template of the directive.
* The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
* content has access to the properties on the scope from which it was taken, even if the directive
* has isolated scope.
* See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
*
* This makes it possible for the widget to have private state for its template, while the transcluded
* content has access to its originating scope.
*
* <div class="alert alert-warning">
* **Note:** When testing an element transclude directive you must not place the directive at the root of the
* DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
* Testing Transclusion Directives}.
* </div>
*
* #### Transclusion Functions
*
* When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
* function** to the directive's `link` function and `controller`. This transclusion function is a special
* **linking function** that will return the compiled contents linked to a new transclusion scope.
*
* <div class="alert alert-info">
* If you are just using {@link ngTransclude} then you don't need to worry about this function, since
* ngTransclude will deal with it for us.
* </div>
*
* If you want to manually control the insertion and removal of the transcluded content in your directive
* then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
* object that contains the compiled DOM, which is linked to the correct transclusion scope.
*
* When you call a transclusion function you can pass in a **clone attach function**. This function accepts
* two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
* content and the `scope` is the newly created transclusion scope, to which the clone is bound.
*
* <div class="alert alert-info">
* **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
* since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
* </div>
*
* It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
* attach function**:
*
* ```js
* var transcludedContent, transclusionScope;
*
* $transclude(function(clone, scope) {
* element.append(clone);
* transcludedContent = clone;
* transclusionScope = scope;
* });
* ```
*
* Later, if you want to remove the transcluded content from your DOM then you should also destroy the
* associated transclusion scope:
*
* ```js
* transcludedContent.remove();
* transclusionScope.$destroy();
* ```
*
* <div class="alert alert-info">
* **Best Practice**: if you intend to add and remove transcluded content manually in your directive
* (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
* then you are also responsible for calling `$destroy` on the transclusion scope.
* </div>
*
* The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
* automatically destroy their transluded clones as necessary so you do not need to worry about this if
* you are simply using {@link ngTransclude} to inject the transclusion into your directive.
*
*
* #### Transclusion Scopes
*
* When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
* scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
* when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
* was taken.
*
* For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
* like this:
*
* ```html
* <div ng-app>
* <div isolate>
* <div transclusion>
* </div>
* </div>
* </div>
* ```
*
* The `$parent` scope hierarchy will look like this:
*
* ```
* - $rootScope
* - isolate
* - transclusion
* ```
*
* but the scopes will inherit prototypically from different scopes to their `$parent`.
*
* ```
* - $rootScope
* - transclusion
* - isolate
* ```
*
*
* ### Attributes
*
* The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
* `link()` or `compile()` functions. It has a variety of uses.
*
* accessing *Normalized attribute names:*
* Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
* the attributes object allows for normalized access to
* the attributes.
*
* * *Directive inter-communication:* All directives share the same instance of the attributes
* object which allows the directives to use the attributes object as inter directive
* communication.
*
* * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
* allowing other directives to read the interpolated value.
*
* * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
* that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
* the only way to easily get the actual value because during the linking phase the interpolation
* hasn't been evaluated yet and so the value is at this time set to `undefined`.
*
* ```js
* function linkingFn(scope, elm, attrs, ctrl) {
* // get the attribute value
* console.log(attrs.ngModel);
*
* // change the attribute
* attrs.$set('ngModel', 'new value');
*
* // observe changes to interpolated attribute
* attrs.$observe('ngModel', function(value) {
* console.log('ngModel has changed value to ' + value);
* });
* }
* ```
*
* ## Example
*
* <div class="alert alert-warning">
* **Note**: Typically directives are registered with `module.directive`. The example below is
* to illustrate how `$compile` works.
* </div>
*
<example module="compileExample">
<file name="index.html">
<script>
angular.module('compileExample', [], function($compileProvider) {
// configure new 'compile' directive by passing a directive
// factory function. The factory function injects the '$compile'
$compileProvider.directive('compile', function($compile) {
// directive factory creates a link function
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
});
})
.controller('GreeterController', ['$scope', function($scope) {
$scope.name = 'Angular';
$scope.html = 'Hello {{name}}';
}]);
</script>
<div ng-controller="GreeterController">
<input ng-model="name"> <br/>
<textarea ng-model="html"></textarea> <br/>
<div compile="html"></div>
</div>
</file>
<file name="protractor.js" type="protractor">
it('should auto compile', function() {
var textarea = $('textarea');
var output = $('div[compile]');
// The initial state reads 'Hello Angular'.
expect(output.getText()).toBe('Hello Angular');
textarea.clear();
textarea.sendKeys('{{name}}!');
expect(output.getText()).toBe('Angular!');
});
</file>
</example>
*
*
* @param {string|DOMElement} element Element or HTML string to compile into a template function.
* @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
*
* <div class="alert alert-danger">
* **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
* e.g. will not use the right outer scope. Please pass the transclude function as a
* `parentBoundTranscludeFn` to the link function instead.
* </div>
*
* @param {number} maxPriority only apply directives lower than given priority (Only effects the
* root element(s), not their children)
* @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
* (a DOM element/tree) to a scope. Where:
*
* * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
* * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
* `template` and call the `cloneAttachFn` function allowing the caller to attach the
* cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
* called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
*
* * `clonedElement` - is a clone of the original `element` passed into the compiler.
* * `scope` - is the current scope with which the linking function is working with.
*
* * `options` - An optional object hash with linking options. If `options` is provided, then the following
* keys may be used to control linking behavior:
*
* * `parentBoundTranscludeFn` - the transclude function made available to
* directives; if given, it will be passed through to the link functions of
* directives found in `element` during compilation.
* * `transcludeControllers` - an object hash with keys that map controller names
* to controller instances; if given, it will make the controllers
* available to directives.
* * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
* the cloned elements; only needed for transcludes that are allowed to contain non html
* elements (e.g. SVG elements). See also the directive.controller property.
*
* Calling the linking function returns the element of the template. It is either the original
* element passed in, or the clone of the element if the `cloneAttachFn` is provided.
*
* After linking the view is not updated until after a call to $digest which typically is done by
* Angular automatically.
*
* If you need access to the bound view, there are two ways to do it:
*
* - If you are not asking the linking function to clone the template, create the DOM element(s)
* before you send them to the compiler and keep this reference around.
* ```js
* var element = $compile('<p>{{total}}</p>')(scope);
* ```
*
* - if on the other hand, you need the element to be cloned, the view reference from the original
* example would not point to the clone, but rather to the original template that was cloned. In
* this case, you can access the clone via the cloneAttachFn:
* ```js
* var templateElement = angular.element('<p>{{total}}</p>'),
* scope = ....;
*
* var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
* //attach the clone to DOM document at the right place
* });
*
* //now we have reference to the cloned DOM via `clonedElement`
* ```
*
*
* For information on how the compiler works, see the
* {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
*/
var $compileMinErr = minErr('$compile');
/**
* @ngdoc provider
* @name $compileProvider
*
* @description
*/
$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
function $CompileProvider($provide, $$sanitizeUriProvider) {
var hasDirectives = {},
Suffix = 'Directive',
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
// 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)$/;
function parseIsolateBindings(scope, directiveName, isController) {
var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
var bindings = {};
forEach(scope, function(definition, scopeName) {
var match = definition.match(LOCAL_REGEXP);
if (!match) {
throw $compileMinErr('iscp',
"Invalid {3} for directive '{0}'." +
" Definition: {... {1}: '{2}' ...}",
directiveName, scopeName, definition,
(isController ? "controller bindings definition" :
"isolate scope definition"));
}
bindings[scopeName] = {
mode: match[1][0],
collection: match[2] === '*',
optional: match[3] === '?',
attrName: match[4] || scopeName
};
});
return bindings;
}
function parseDirectiveBindings(directive, directiveName) {
var bindings = {
isolateScope: null,
bindToController: null
};
if (isObject(directive.scope)) {
if (directive.bindToController === true) {
bindings.bindToController = parseIsolateBindings(directive.scope,
directiveName, true);
bindings.isolateScope = {};
} else {
bindings.isolateScope = parseIsolateBindings(directive.scope,
directiveName, false);
}
}
if (isObject(directive.bindToController)) {
bindings.bindToController =
parseIsolateBindings(directive.bindToController, directiveName, true);
}
if (isObject(bindings.bindToController)) {
var controller = directive.controller;
var controllerAs = directive.controllerAs;
if (!controller) {
// There is no controller, there may or may not be a controllerAs property
throw $compileMinErr('noctrl',
"Cannot bind to controller without directive '{0}'s controller.",
directiveName);
} else if (!identifierForController(controller, controllerAs)) {
// There is a controller, but no identifier or controllerAs property
throw $compileMinErr('noident',
"Cannot bind to controller without identifier for directive '{0}'.",
directiveName);
}
}
return bindings;
}
function assertValidDirectiveName(name) {
var letter = name.charAt(0);
if (!letter || letter !== lowercase(letter)) {
throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
}
if (name !== name.trim()) {
throw $compileMinErr('baddir',
"Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
name);
}
}
/**
* @ngdoc method
* @name $compileProvider#directive
* @kind function
*
* @description
* Register a new directive with the compiler.
*
* @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
* will match as <code>ng-bind</code>), or an object map of directives where the keys are the
* names and the values are the factories.
* @param {Function|Array} directiveFactory An injectable directive factory function. See
* {@link guide/directive} for more info.
* @returns {ng.$compileProvider} Self for chaining.
*/
this.directive = function registerDirective(name, directiveFactory) {
assertNotHasOwnProperty(name, 'directive');
if (isString(name)) {
assertValidDirectiveName(name);
assertArg(directiveFactory, 'directiveFactory');
if (!hasDirectives.hasOwnProperty(name)) {
hasDirectives[name] = [];
$provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
function($injector, $exceptionHandler) {
var directives = [];
forEach(hasDirectives[name], function(directiveFactory, index) {
try {
var directive = $injector.invoke(directiveFactory);
if (isFunction(directive)) {
directive = { compile: valueFn(directive) };
} else if (!directive.compile && directive.link) {
directive.compile = valueFn(directive.link);
}
directive.priority = directive.priority || 0;
directive.index = index;
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;
}
directives.push(directive);
} catch (e) {
$exceptionHandler(e);
}
});
return directives;
}]);
}
hasDirectives[name].push(directiveFactory);
} else {
forEach(name, reverseParams(registerDirective));
}
return this;
};
/**
* @ngdoc method
* @name $compileProvider#aHrefSanitizationWhitelist
* @kind function
*
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during a[href] sanitization.
*
* The sanitization is a security measure aimed at preventing XSS attacks via html links.
*
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into
* an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
*
* @param {RegExp=} regexp New regexp to whitelist urls with.
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
* chaining otherwise.
*/
this.aHrefSanitizationWhitelist = function(regexp) {
if (isDefined(regexp)) {
$$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
return this;
} else {
return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
}
};
/**
* @ngdoc method
* @name $compileProvider#imgSrcSanitizationWhitelist
* @kind function
*
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during img[src] sanitization.
*
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
*
* Any url about to be assigned to img[src] via data-binding is first normalized and turned into
* an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
*
* @param {RegExp=} regexp New regexp to whitelist urls with.
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
* chaining otherwise.
*/
this.imgSrcSanitizationWhitelist = function(regexp) {
if (isDefined(regexp)) {
$$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
return this;
} else {
return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
}
};
/**
* @ngdoc method
* @name $compileProvider#debugInfoEnabled
*
* @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
* current debugInfoEnabled state
* @returns {*} current value if used as getter or itself (chaining) if used as setter
*
* @kind function
*
* @description
* Call this method to enable/disable various debug runtime information in the compiler such as adding
* binding information and a reference to the current scope on to DOM elements.
* If enabled, the compiler will add the following to DOM elements that have been bound to the scope
* * `ng-binding` CSS class
* * `$binding` data property containing an array of the binding expressions
*
* You may want to disable this in production for a significant performance boost. See
* {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
*
* The default value is true.
*/
var debugInfoEnabled = true;
this.debugInfoEnabled = function(enabled) {
if (isDefined(enabled)) {
debugInfoEnabled = enabled;
return this;
}
return debugInfoEnabled;
};
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
'$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
$controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
var Attributes = function(element, attributesToCopy) {
if (attributesToCopy) {
var keys = Object.keys(attributesToCopy);
var i, l, key;
for (i = 0, l = keys.length; i < l; i++) {
key = keys[i];
this[key] = attributesToCopy[key];
}
} else {
this.$attr = {};
}
this.$$element = element;
};
Attributes.prototype = {
/**
* @ngdoc method
* @name $compile.directive.Attributes#$normalize
* @kind function
*
* @description
* Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
* `data-`) to its normalized, camelCase form.
*
* Also there is special case for Moz prefix starting with upper case letter.
*
* For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
*
* @param {string} name Name to normalize
*/
$normalize: directiveNormalize,
/**
* @ngdoc method
* @name $compile.directive.Attributes#$addClass
* @kind function
*
* @description
* Adds the CSS class value specified by the classVal parameter to the element. If animations
* are enabled then an animation will be triggered for the class addition.
*
* @param {string} classVal The className value that will be added to the element
*/
$addClass: function(classVal) {
if (classVal && classVal.length > 0) {
$animate.addClass(this.$$element, classVal);
}
},
/**
* @ngdoc method
* @name $compile.directive.Attributes#$removeClass
* @kind function
*
* @description
* Removes the CSS class value specified by the classVal parameter from the element. If
* animations are enabled then an animation will be triggered for the class removal.
*
* @param {string} classVal The className value that will be removed from the element
*/
$removeClass: function(classVal) {
if (classVal && classVal.length > 0) {
$animate.removeClass(this.$$element, classVal);
}
},
/**
* @ngdoc method
* @name $compile.directive.Attributes#$updateClass
* @kind function
*
* @description
* Adds and removes the appropriate CSS class values to the element based on the difference
* between the new and old CSS class values (specified as newClasses and oldClasses).
*
* @param {string} newClasses The current CSS className value
* @param {string} oldClasses The former CSS className value
*/
$updateClass: function(newClasses, oldClasses) {
var toAdd = tokenDifference(newClasses, oldClasses);
if (toAdd && toAdd.length) {
$animate.addClass(this.$$element, toAdd);
}
var toRemove = tokenDifference(oldClasses, newClasses);
if (toRemove && toRemove.length) {
$animate.removeClass(this.$$element, toRemove);
}
},
/**
* Set a normalized attribute on the element in a way such that all directives
* can share the attribute. This function properly handles boolean attributes.
* @param {string} key Normalized key. (ie ngAttribute)
* @param {string|boolean} value The value to set. If `null` attribute will be deleted.
* @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
* Defaults to true.
* @param {string=} attrName Optional none normalized name. Defaults to key.
*/
$set: function(key, value, writeAttr, attrName) {
// TODO: decide whether or not to throw an error if "class"
//is set through this function since it may cause $updateClass to
//become unstable.
var node = this.$$element[0],
booleanKey = getBooleanAttrName(node, key),
aliasedKey = getAliasedAttrName(node, key),
observer = key,
nodeName;
if (booleanKey) {
this.$$element.prop(key, value);
attrName = booleanKey;
} else if (aliasedKey) {
this[aliasedKey] = value;
observer = aliasedKey;
}
this[key] = value;
// translate normalized key to actual key
if (attrName) {
this.$attr[key] = attrName;
} else {
attrName = this.$attr[key];
if (!attrName) {
this.$attr[key] = attrName = snake_case(key, '-');
}
}
nodeName = nodeName_(this.$$element);
if ((nodeName === 'a' && key === 'href') ||
(nodeName === 'img' && key === 'src')) {
// sanitize a[href] and img[src] values
this[key] = value = $$sanitizeUri(value, key === 'src');
} else if (nodeName === 'img' && key === 'srcset') {
// sanitize img[srcset] values
var result = "";
// first check if there are spaces because it's not the same pattern
var trimmedSrcset = trim(value);
// ( 999x ,| 999w ,| ,|, )
var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
// split srcset into tuple of uri and descriptor except for the last item
var rawUris = trimmedSrcset.split(pattern);
// for each tuples
var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
for (var i = 0; i < nbrUrisWith2parts; i++) {
var innerIdx = i * 2;
// sanitize the uri
result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
// add the descriptor
result += (" " + trim(rawUris[innerIdx + 1]));
}
// split the last item into uri and descriptor
var lastTuple = trim(rawUris[i * 2]).split(/\s/);
// sanitize the last uri
result += $$sanitizeUri(trim(lastTuple[0]), true);
// and add the last descriptor if any
if (lastTuple.length === 2) {
result += (" " + trim(lastTuple[1]));
}
this[key] = value = result;
}
if (writeAttr !== false) {
if (value === null || value === undefined) {
this.$$element.removeAttr(attrName);
} else {
this.$$element.attr(attrName, value);
}
}
// fire observers
var $$observers = this.$$observers;
$$observers && forEach($$observers[observer], function(fn) {
try {
fn(value);
} catch (e) {
$exceptionHandler(e);
}
});
},
/**
* @ngdoc method
* @name $compile.directive.Attributes#$observe
* @kind function
*
* @description
* Observes an interpolated attribute.
*
* The observer function will be invoked once during the next `$digest` following
* compilation. The observer is then invoked whenever the interpolated value
* changes.
*
* @param {string} key Normalized key. (ie ngAttribute) .
* @param {function(interpolatedValue)} fn Function that will be called whenever
the interpolated value of the attribute changes.
* See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
* @returns {function()} Returns a deregistration function for this observer.
*/
$observe: function(key, fn) {
var attrs = this,
$$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
listeners = ($$observers[key] || ($$observers[key] = []));
listeners.push(fn);
$rootScope.$evalAsync(function() {
if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
// no one registered attribute interpolation function, so lets call it manually
fn(attrs[key]);
}
});
return function() {
arrayRemove(listeners, fn);
};
}
};
function safeAddClass($element, className) {
try {
$element.addClass(className);
} catch (e) {
// ignore, since it means that we are trying to set class on
// SVG element, where class name is read-only.
}
}
var startSymbol = $interpolate.startSymbol(),
endSymbol = $interpolate.endSymbol(),
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
? identity
: function denormalizeTemplate(template) {
return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
},
NG_ATTR_BINDING = /^ngAttr[A-Z]/;
compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
var bindings = $element.data('$binding') || [];
if (isArray(binding)) {
bindings = bindings.concat(binding);
} else {
bindings.push(binding);
}
$element.data('$binding', bindings);
} : noop;
compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
safeAddClass($element, 'ng-binding');
} : noop;
compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
$element.data(dataName, scope);
} : noop;
compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
} : noop;
return compile;
//================================
function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
previousCompileContext) {
if (!($compileNodes instanceof jqLite)) {
// jquery always rewraps, whereas we need to preserve the original selector so that we can
// modify it.
$compileNodes = jqLite($compileNodes);
}
// 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 <span>
forEach($compileNodes, function(node, index) {
if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
$compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
}
});
var compositeLinkFn =
compileNodes($compileNodes, transcludeFn, $compileNodes,
maxPriority, ignoreDirective, previousCompileContext);
compile.$$addScopeClass($compileNodes);
var namespace = null;
return function publicLinkFn(scope, cloneConnectFn, options) {
assertArg(scope, 'scope');
options = options || {};
var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
transcludeControllers = options.transcludeControllers,
futureParentElement = options.futureParentElement;
// When `parentBoundTranscludeFn` is passed, it is a
// `controllersBoundTransclude` function (it was previously passed
// as `transclude` to directive.link) so we must unwrap it to get
// its `boundTranscludeFn`
if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
}
if (!namespace) {
namespace = detectNamespaceForChildElements(futureParentElement);
}
var $linkNode;
if (namespace !== 'html') {
// When using a directive with replace:true and templateUrl the $compileNodes
// (or a child element inside of them)
// might change, so we need to recreate the namespace adapted compileNodes
// for call to the link function.
// Note: This will already clone the nodes...
$linkNode = jqLite(
wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
);
} else if (cloneConnectFn) {
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
// and sometimes changes the structure of the DOM.
$linkNode = JQLitePrototype.clone.call($compileNodes);
} else {
$linkNode = $compileNodes;
}
if (transcludeControllers) {
for (var controllerName in transcludeControllers) {
$linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
}
}
compile.$$addScopeInfo($linkNode, scope);
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
return $linkNode;
};
}
function detectNamespaceForChildElements(parentElement) {
// TODO: Make this detect MathML as well...
var node = parentElement && parentElement[0];
if (!node) {
return 'html';
} else {
return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
}
}
/**
* Compile function matches each node in nodeList against the directives. Once all directives
* for a particular node are collected their compile functions are executed. The compile
* functions return values - the linking functions - are combined into a composite linking
* function, which is the a linking function for the node.
*
* @param {NodeList} nodeList an array of nodes or NodeList to compile
* @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
* scope argument is auto-generated to the new child of the transcluded parent scope.
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
* the rootElement must be set the jqLite collection of the compile root. This is
* needed so that the jqLite collection items can be replaced with widgets.
* @param {number=} maxPriority Max directive priority.
* @returns {Function} A composite linking function of all of the matched directives or null.
*/
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
previousCompileContext) {
var linkFns = [],
attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
for (var i = 0; i < nodeList.length; i++) {
attrs = new Attributes();
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
ignoreDirective);
nodeLinkFn = (directives.length)
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
null, [], [], previousCompileContext)
: null;
if (nodeLinkFn && nodeLinkFn.scope) {
compile.$$addScopeClass(attrs.$$element);
}
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
!(childNodes = nodeList[i].childNodes) ||
!childNodes.length)
? null
: compileNodes(childNodes,
nodeLinkFn ? (
(nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
&& nodeLinkFn.transclude) : transcludeFn);
if (nodeLinkFn || childLinkFn) {
linkFns.push(i, nodeLinkFn, childLinkFn);
linkFnFound = true;
nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
}
//use the previous context only for the first element in the virtual group
previousCompileContext = null;
}
// return a linking function if we have found anything, null otherwise
return linkFnFound ? compositeLinkFn : null;
function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
var stableNodeList;
if (nodeLinkFnFound) {
// copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
// offsets don't get screwed up
var nodeListLength = nodeList.length;
stableNodeList = new Array(nodeListLength);
// create a sparse array by only copying the elements which have a linkFn
for (i = 0; i < linkFns.length; i+=3) {
idx = linkFns[i];
stableNodeList[idx] = nodeList[idx];
}
} else {
stableNodeList = nodeList;
}
for (i = 0, ii = linkFns.length; i < ii;) {
node = stableNodeList[linkFns[i++]];
nodeLinkFn = linkFns[i++];
childLinkFn = linkFns[i++];
if (nodeLinkFn) {
if (nodeLinkFn.scope) {
childScope = scope.$new();
compile.$$addScopeInfo(jqLite(node), childScope);
var destroyBindings = nodeLinkFn.$$destroyBindings;
if (destroyBindings) {
nodeLinkFn.$$destroyBindings = null;
childScope.$on('$destroyed', destroyBindings);
}
} else {
childScope = scope;
}
if (nodeLinkFn.transcludeOnThisElement) {
childBoundTranscludeFn = createBoundTranscludeFn(
scope, nodeLinkFn.transclude, parentBoundTranscludeFn,
nodeLinkFn.elementTranscludeOnThisElement);
} else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
childBoundTranscludeFn = parentBoundTranscludeFn;
} else if (!parentBoundTranscludeFn && transcludeFn) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
} else {
childBoundTranscludeFn = null;
}
nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn,
nodeLinkFn);
} else if (childLinkFn) {
childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
}
}
}
}
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
if (!transcludedScope) {
transcludedScope = scope.$new(false, containingScope);
transcludedScope.$$transcluded = true;
}
return transcludeFn(transcludedScope, cloneFn, {
parentBoundTranscludeFn: previousBoundTranscludeFn,
transcludeControllers: controllers,
futureParentElement: futureParentElement
});
};
return boundTranscludeFn;
}
/**
* Looks for directives on the given node and adds them to the directive collection which is
* sorted.
*
* @param node Node to search.
* @param directives An array to which the directives are added to. This array is sorted before
* the function returns.
* @param attrs The shared attrs object which is used to populate the normalized attributes.
* @param {number=} maxPriority Max directive priority.
*/
function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
var nodeType = node.nodeType,
attrsMap = attrs.$attr,
match,
className;
switch (nodeType) {
case NODE_TYPE_ELEMENT: /* Element */
// use the node name: <directive>
addDirective(directives,
directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
// iterate over the attributes
for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
var attrStartName = false;
var attrEndName = false;
attr = nAttrs[j];
name = attr.name;
value = trim(attr.value);
// support ngAttr attribute binding
ngAttrName = directiveNormalize(name);
if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
name = name.replace(PREFIX_REGEXP, '')
.substr(8).replace(/_(.)/g, function(match, letter) {
return letter.toUpperCase();
});
}
var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
if (directiveIsMultiElement(directiveNName)) {
if (ngAttrName === directiveNName + 'Start') {
attrStartName = name;
attrEndName = name.substr(0, name.length - 5) + 'end';
name = name.substr(0, name.length - 6);
}
}
nName = directiveNormalize(name.toLowerCase());
attrsMap[nName] = name;
if (isNgAttr || !attrs.hasOwnProperty(nName)) {
attrs[nName] = value;
if (getBooleanAttrName(node, nName)) {
attrs[nName] = true; // presence means true
}
}
addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
attrEndName);
}
// use class as directive
className = node.className;
if (isObject(className)) {
// Maybe SVGAnimatedString
className = className.animVal;
}
if (isString(className) && className !== '') {
while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
nName = directiveNormalize(match[2]);
if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
attrs[nName] = trim(match[3]);
}
className = className.substr(match.index + match[0].length);
}
}
break;
case NODE_TYPE_TEXT: /* Text Node */
addTextInterpolateDirective(directives, node.nodeValue);
break;
case NODE_TYPE_COMMENT: /* Comment */
try {
match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
if (match) {
nName = directiveNormalize(match[1]);
if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
attrs[nName] = trim(match[2]);
}
}
} catch (e) {
// turns out that under some circumstances IE9 throws errors when one attempts to read
// comment's node value.
// Just ignore it and continue. (Can't seem to reproduce in test case.)
}
break;
}
directives.sort(byPriority);
return directives;
}
/**
* Given a node with an directive-start it collects all of the siblings until it finds
* directive-end.
* @param node
* @param attrStart
* @param attrEnd
* @returns {*}
*/
function groupScan(node, attrStart, attrEnd) {
var nodes = [];
var depth = 0;
if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
do {
if (!node) {
throw $compileMinErr('uterdir',
"Unterminated attribute, found '{0}' but no matching '{1}' found.",
attrStart, attrEnd);
}
if (node.nodeType == NODE_TYPE_ELEMENT) {
if (node.hasAttribute(attrStart)) depth++;
if (node.hasAttribute(attrEnd)) depth--;
}
nodes.push(node);
node = node.nextSibling;
} while (depth > 0);
} else {
nodes.push(node);
}
return jqLite(nodes);
}
/**
* Wrapper for linking function which converts normal linking function into a grouped
* linking function.
* @param linkFn
* @param attrStart
* @param attrEnd
* @returns {Function}
*/
function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
return function(scope, element, attrs, controllers, transcludeFn) {
element = groupScan(element[0], attrStart, attrEnd);
return linkFn(scope, element, attrs, controllers, transcludeFn);
};
}
/**
* Once the directives have been collected, their compile functions are executed. This method
* is responsible for inlining directive templates as well as terminating the application
* of the directives if the terminal directive has been reached.
*
* @param {Array} directives Array of collected directives to execute their compile function.
* this needs to be pre-sorted by priority order.
* @param {Node} compileNode The raw DOM node to apply the compile functions to
* @param {Object} templateAttrs The shared attribute function
* @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
* scope argument is auto-generated to the new
* child of the transcluded parent scope.
* @param {JQLite} jqCollection If we are working on the root of the compile tree then this
* argument has the root jqLite array so that we can replace nodes
* on it.
* @param {Object=} originalReplaceDirective An optional directive that will be ignored when
* compiling the transclusion.
* @param {Array.<Function>} preLinkFns
* @param {Array.<Function>} postLinkFns
* @param {Object} previousCompileContext Context used for previous compilation of the current
* node
* @returns {Function} linkFn
*/
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
previousCompileContext) {
previousCompileContext = previousCompileContext || {};
var terminalPriority = -Number.MAX_VALUE,
newScopeDirective,
controllerDirectives = previousCompileContext.controllerDirectives,
newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
templateDirective = previousCompileContext.templateDirective,
nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
hasTranscludeDirective = false,
hasTemplate = false,
hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
$compileNode = templateAttrs.$$element = jqLite(compileNode),
directive,
directiveName,
$template,
replaceDirective = originalReplaceDirective,
childTranscludeFn = transcludeFn,
linkFn,
directiveValue;
// executes all directives on the current element
for (var i = 0, ii = directives.length; i < ii; i++) {
directive = directives[i];
var attrStart = directive.$$start;
var attrEnd = directive.$$end;
// collect multiblock sections
if (attrStart) {
$compileNode = groupScan(compileNode, attrStart, attrEnd);
}
$template = undefined;
if (terminalPriority > directive.priority) {
break; // prevent further processing of directives
}
if (directiveValue = directive.scope) {
// skip the check for directives with async templates, we'll check the derived sync
// directive when the template arrives
if (!directive.templateUrl) {
if (isObject(directiveValue)) {
// This directive is trying to add an isolated scope.
// Check that there is no scope of any kind already
assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
directive, $compileNode);
newIsolateScopeDirective = directive;
} else {
// This directive is trying to add a child scope.
// Check that there is no isolated scope already
assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
$compileNode);
}
}
newScopeDirective = newScopeDirective || directive;
}
directiveName = directive.name;
if (!directive.templateUrl && directive.controller) {
directiveValue = directive.controller;
controllerDirectives = controllerDirectives || createMap();
assertNoDuplicate("'" + directiveName + "' controller",
controllerDirectives[directiveName], directive, $compileNode);
controllerDirectives[directiveName] = directive;
}
if (directiveValue = directive.transclude) {
hasTranscludeDirective = true;
// Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
// This option should only be used by directives that know how to safely handle element transclusion,
// where the transcluded nodes are added or replaced after linking.
if (!directive.$$tlb) {
assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
nonTlbTranscludeDirective = directive;
}
if (directiveValue == 'element') {
hasElementTranscludeDirective = true;
terminalPriority = directive.priority;
$template = $compileNode;
$compileNode = templateAttrs.$$element =
jqLite(document.createComment(' ' + directiveName + ': ' +
templateAttrs[directiveName] + ' '));
compileNode = $compileNode[0];
replaceWith(jqCollection, sliceArgs($template), compileNode);
childTranscludeFn = compile($template, transcludeFn, terminalPriority,
replaceDirective && replaceDirective.name, {
// Don't pass in:
// - controllerDirectives - otherwise we'll create duplicates controllers
// - newIsolateScopeDirective or templateDirective - combining templates with
// element transclusion doesn't make sense.
//
// We need only nonTlbTranscludeDirective so that we prevent putting transclusion
// on the same element more than once.
nonTlbTranscludeDirective: nonTlbTranscludeDirective
});
} else {
$template = jqLite(jqLiteClone(compileNode)).contents();
$compileNode.empty(); // clear contents
childTranscludeFn = compile($template, transcludeFn);
}
}
if (directive.template) {
hasTemplate = true;
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
directiveValue = (isFunction(directive.template))
? directive.template($compileNode, templateAttrs)
: directive.template;
directiveValue = denormalizeTemplate(directiveValue);
if (directive.replace) {
replaceDirective = directive;
if (jqLiteIsTextNode(directiveValue)) {
$template = [];
} else {
$template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
}
compileNode = $template[0];
if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
throw $compileMinErr('tplrt',
"Template for directive '{0}' must have exactly one root element. {1}",
directiveName, '');
}
replaceWith(jqCollection, $compileNode, compileNode);
var newTemplateAttrs = {$attr: {}};
// combine directives from the original node and from the template:
// - take the array of directives for this element
// - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
// - collect directives from the template and sort them by priority
// - combine directives as: processed + template + unprocessed
var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
if (newIsolateScopeDirective) {
markDirectivesAsIsolate(templateDirectives);
}
directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
ii = directives.length;
} else {
$compileNode.html(directiveValue);
}
}
if (directive.templateUrl) {
hasTemplate = true;
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
if (directive.replace) {
replaceDirective = directive;
}
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
controllerDirectives: controllerDirectives,
newIsolateScopeDirective: newIsolateScopeDirective,
templateDirective: templateDirective,
nonTlbTranscludeDirective: nonTlbTranscludeDirective
});
ii = directives.length;
} else if (directive.compile) {
try {
linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
if (isFunction(linkFn)) {
addLinkFns(null, linkFn, attrStart, attrEnd);
} else if (linkFn) {
addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
}
} catch (e) {
$exceptionHandler(e, startingTag($compileNode));
}
}
if (directive.terminal) {
nodeLinkFn.terminal = true;
terminalPriority = Math.max(terminalPriority, directive.priority);
}
}
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
nodeLinkFn.templateOnThisElement = hasTemplate;
nodeLinkFn.transclude = childTranscludeFn;
previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
// might be normal or delayed nodeLinkFn depending on if templateUrl is present
return nodeLinkFn;
////////////////////
function addLinkFns(pre, post, attrStart, attrEnd) {
if (pre) {
if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
pre.require = directive.require;
pre.directiveName = directiveName;
if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
pre = cloneAndAnnotateFn(pre, {isolateScope: true});
}
preLinkFns.push(pre);
}
if (post) {
if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
post.require = directive.require;
post.directiveName = directiveName;
if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
post = cloneAndAnnotateFn(post, {isolateScope: true});
}
postLinkFns.push(post);
}
}
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);
}
}
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,
thisLinkFn) {
var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
attrs;
if (compileNode === linkNode) {
attrs = templateAttrs;
$element = templateAttrs.$$element;
} else {
$element = jqLite(linkNode);
attrs = new Attributes($element, templateAttrs);
}
if (newIsolateScopeDirective) {
isolateScope = scope.$new(true);
}
if (boundTranscludeFn) {
// track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
// is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
transcludeFn = controllersBoundTransclude;
transcludeFn.$$boundTransclude = boundTranscludeFn;
}
if (controllerDirectives) {
elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
}
if (newIsolateScopeDirective) {
// Initialize isolate scope bindings for new isolate scope directive.
compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
templateDirective === newIsolateScopeDirective.$$originalDirective)));
compile.$$addScopeClass($element, true);
isolateScope.$$isolateBindings =
newIsolateScopeDirective.$$isolateBindings;
initializeDirectiveBindings(scope, attrs, isolateScope,
isolateScope.$$isolateBindings,
newIsolateScopeDirective, isolateScope);
}
if (elementControllers) {
// Initialize bindToController bindings for new/isolate scopes
var scopeDirective = newIsolateScopeDirective || newScopeDirective;
var bindings;
var controllerForBindings;
if (scopeDirective && elementControllers[scopeDirective.name]) {
bindings = scopeDirective.$$bindings.bindToController;
controller = elementControllers[scopeDirective.name];
if (controller && controller.identifier && bindings) {
controllerForBindings = controller;
thisLinkFn.$$destroyBindings =
initializeDirectiveBindings(scope, attrs, controller.instance,
bindings, scopeDirective);
}
}
for (i in elementControllers) {
controller = elementControllers[i];
var controllerResult = controller();
if (controllerResult !== controller.instance) {
controller.instance = controllerResult;
$element.data('$' + directive.name + 'Controller', controllerResult);
if (controller === controllerForBindings) {
// Remove and re-install bindToController bindings
thisLinkFn.$$destroyBindings();
thisLinkFn.$$destroyBindings =
initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
}
}
}
}
// PRELINKING
for (i = 0, ii = preLinkFns.length; i < ii; i++) {
linkFn = preLinkFns[i];
invokeLinkFn(linkFn,
linkFn.isolateScope ? isolateScope : scope,
$element,
attrs,
linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
transcludeFn
);
}
// RECURSION
// We only pass the isolate scope, if the isolate directive has a template,
// otherwise the child elements do not belong to the isolate directive.
var scopeToChild = scope;
if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
scopeToChild = isolateScope;
}
childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
// POSTLINKING
for (i = postLinkFns.length - 1; i >= 0; i--) {
linkFn = postLinkFns[i];
invokeLinkFn(linkFn,
linkFn.isolateScope ? isolateScope : scope,
$element,
attrs,
linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
transcludeFn
);
}
// This is the function that is injected as `$transclude`.
// Note: all arguments are optional!
function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
var transcludeControllers;
// No scope passed in:
if (!isScope(scope)) {
futureParentElement = cloneAttachFn;
cloneAttachFn = scope;
scope = undefined;
}
if (hasElementTranscludeDirective) {
transcludeControllers = elementControllers;
}
if (!futureParentElement) {
futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
}
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
}
}
}
function markDirectivesAsIsolate(directives) {
// mark all directives as needing isolate scope.
for (var j = 0, jj = directives.length; j < jj; j++) {
directives[j] = inherit(directives[j], {$$isolateScope: true});
}
}
/**
* looks up the directive and decorates it with exception handling and proper parameters. We
* call this the boundDirective.
*
* @param {string} name name of the directive to look up.
* @param {string} location The directive must be found in specific format.
* String containing any of theses characters:
*
* * `E`: element name
* * `A': attribute
* * `C`: class
* * `M`: comment
* @returns {boolean} true if directive was added.
*/
function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
endAttrName) {
if (name === ignoreDirective) return null;
var match = null;
if (hasDirectives.hasOwnProperty(name)) {
for (var directive, directives = $injector.get(name + Suffix),
i = 0, ii = directives.length; i < ii; i++) {
try {
directive = directives[i];
if ((maxPriority === undefined || maxPriority > directive.priority) &&
directive.restrict.indexOf(location) != -1) {
if (startAttrName) {
directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
}
tDirectives.push(directive);
match = directive;
}
} catch (e) { $exceptionHandler(e); }
}
}
return match;
}
/**
* looks up the directive and returns true if it is a multi-element directive,
* and therefore requires DOM nodes between -start and -end markers to be grouped
* together.
*
* @param {string} name name of the directive to look up.
* @returns true if directive was registered as multi-element.
*/
function directiveIsMultiElement(name) {
if (hasDirectives.hasOwnProperty(name)) {
for (var directive, directives = $injector.get(name + Suffix),
i = 0, ii = directives.length; i < ii; i++) {
directive = directives[i];
if (directive.multiElement) {
return true;
}
}
}
return false;
}
/**
* When the element is replaced with HTML template then the new attributes
* on the template need to be merged with the existing attributes in the DOM.
* The desired effect is to have both of the attributes present.
*
* @param {object} dst destination attributes (original DOM)
* @param {object} src source attributes (from the directive template)
*/
function mergeTemplateAttributes(dst, src) {
var srcAttr = src.$attr,
dstAttr = dst.$attr,
$element = dst.$$element;
// reapply the old attributes to the new element
forEach(dst, function(value, key) {
if (key.charAt(0) != '$') {
if (src[key] && src[key] !== value) {
value += (key === 'style' ? ';' : ' ') + src[key];
}
dst.$set(key, value, true, srcAttr[key]);
}
});
// copy the new attributes on the old attrs object
forEach(src, function(value, key) {
if (key == 'class') {
safeAddClass($element, value);
dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
} else if (key == 'style') {
$element.attr('style', $element.attr('style') + ';' + value);
dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
// `dst` will never contain hasOwnProperty as DOM parser won't let it.
// You will get an "InvalidCharacterError: DOM Exception 5" error if you
// have an attribute like "has-own-property" or "data-has-own-property", etc.
} else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
dst[key] = value;
dstAttr[key] = srcAttr[key];
}
});
}
function compileTemplateUrl(directives, $compileNode, tAttrs,
$rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
var linkQueue = [],
afterTemplateNodeLinkFn,
afterTemplateChildLinkFn,
beforeTemplateCompileNode = $compileNode[0],
origAsyncDirective = directives.shift(),
derivedSyncDirective = inherit(origAsyncDirective, {
templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
}),
templateUrl = (isFunction(origAsyncDirective.templateUrl))
? origAsyncDirective.templateUrl($compileNode, tAttrs)
: origAsyncDirective.templateUrl,
templateNamespace = origAsyncDirective.templateNamespace;
$compileNode.empty();
$templateRequest($sce.getTrustedResourceUrl(templateUrl))
.then(function(content) {
var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
content = denormalizeTemplate(content);
if (origAsyncDirective.replace) {
if (jqLiteIsTextNode(content)) {
$template = [];
} else {
$template = removeComments(wrapTemplate(templateNamespace, trim(content)));
}
compileNode = $template[0];
if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
throw $compileMinErr('tplrt',
"Template for directive '{0}' must have exactly one root element. {1}",
origAsyncDirective.name, templateUrl);
}
tempTemplateAttrs = {$attr: {}};
replaceWith($rootElement, $compileNode, compileNode);
var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
if (isObject(origAsyncDirective.scope)) {
markDirectivesAsIsolate(templateDirectives);
}
directives = templateDirectives.concat(directives);
mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
} else {
compileNode = beforeTemplateCompileNode;
$compileNode.html(content);
}
directives.unshift(derivedSyncDirective);
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
previousCompileContext);
forEach($rootElement, function(node, i) {
if (node == compileNode) {
$rootElement[i] = $compileNode[0];
}
});
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
while (linkQueue.length) {
var scope = linkQueue.shift(),
beforeTemplateLinkNode = linkQueue.shift(),
linkRootElement = linkQueue.shift(),
boundTranscludeFn = linkQueue.shift(),
linkNode = $compileNode[0];
if (scope.$$destroyed) continue;
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
var oldClasses = beforeTemplateLinkNode.className;
if (!(previousCompileContext.hasElementTranscludeDirective &&
origAsyncDirective.replace)) {
// it was cloned therefore we have to clone as well.
linkNode = jqLiteClone(compileNode);
}
replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
// Copy in CSS classes from original node
safeAddClass(jqLite(linkNode), oldClasses);
}
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
} else {
childBoundTranscludeFn = boundTranscludeFn;
}
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
childBoundTranscludeFn, afterTemplateNodeLinkFn);
}
linkQueue = null;
});
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
var childBoundTranscludeFn = boundTranscludeFn;
if (scope.$$destroyed) return;
if (linkQueue) {
linkQueue.push(scope,
node,
rootElement,
childBoundTranscludeFn);
} else {
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
}
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn,
afterTemplateNodeLinkFn);
}
};
}
/**
* Sorting function for bound directives.
*/
function byPriority(a, b) {
var diff = b.priority - a.priority;
if (diff !== 0) return diff;
if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
return a.index - b.index;
}
function assertNoDuplicate(what, previousDirective, directive, element) {
if (previousDirective) {
throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
previousDirective.name, directive.name, what, startingTag(element));
}
}
function addTextInterpolateDirective(directives, text) {
var interpolateFn = $interpolate(text, true);
if (interpolateFn) {
directives.push({
priority: 0,
compile: function textInterpolateCompileFn(templateNode) {
var templateNodeParent = templateNode.parent(),
hasCompileParent = !!templateNodeParent.length;
// When transcluding a template that has bindings in the root
// we don't have a parent and thus need to add the class during linking fn.
if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
return function textInterpolateLinkFn(scope, node) {
var parent = node.parent();
if (!hasCompileParent) compile.$$addBindingClass(parent);
compile.$$addBindingInfo(parent, interpolateFn.expressions);
scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
node[0].nodeValue = value;
});
};
}
});
}
}
function wrapTemplate(type, template) {
type = lowercase(type || 'html');
switch (type) {
case 'svg':
case 'math':
var wrapper = document.createElement('div');
wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
return wrapper.childNodes[0].childNodes;
default:
return template;
}
}
function getTrustedContext(node, attrNormalizedName) {
if (attrNormalizedName == "srcdoc") {
return $sce.HTML;
}
var tag = nodeName_(node);
// maction[xlink:href] can source SVG. It's not limited to <maction>.
if (attrNormalizedName == "xlinkHref" ||
(tag == "form" && attrNormalizedName == "action") ||
(tag != "img" && (attrNormalizedName == "src" ||
attrNormalizedName == "ngSrc"))) {
return $sce.RESOURCE_URL;
}
}
function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
var trustedContext = getTrustedContext(node, name);
allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
// no interpolation found -> ignore
if (!interpolateFn) return;
if (name === "multiple" && nodeName_(node) === "select") {
throw $compileMinErr("selmulti",
"Binding to the 'multiple' attribute is not supported. Element: {0}",
startingTag(node));
}
directives.push({
priority: 100,
compile: function() {
return {
pre: function attrInterpolatePreLinkFn(scope, element, attr) {
var $$observers = (attr.$$observers || (attr.$$observers = {}));
if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
throw $compileMinErr('nodomevents',
"Interpolations for HTML DOM event attributes are disallowed. Please use the " +
"ng- versions (such as ng-click instead of onclick) instead.");
}
// If the attribute has changed since last $interpolate()ed
var newValue = attr[name];
if (newValue !== value) {
// we need to interpolate again since the attribute value has been updated
// (e.g. by another directive's compile function)
// ensure unset/empty values make interpolateFn falsy
interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
value = newValue;
}
// if attribute was updated so that there is no interpolation going on we don't want to
// register any observers
if (!interpolateFn) return;
// initialize attr object so that it's ready in case we need the value for isolate
// scope initialization, otherwise the value would not be available from isolate
// directive's linking fn during linking phase
attr[name] = interpolateFn(scope);
($$observers[name] || ($$observers[name] = [])).$$inter = true;
(attr.$$observers && attr.$$observers[name].$$scope || scope).
$watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
//special case for class attribute addition + removal
//so that class changes can tap into the animation
//hooks provided by the $animate service. Be sure to
//skip animations when the first digest occurs (when
//both the new and the old values are the same) since
//the CSS classes are the non-interpolated values
if (name === 'class' && newValue != oldValue) {
attr.$updateClass(newValue, oldValue);
} else {
attr.$set(name, newValue);
}
});
}
};
}
});
}
/**
* This is a special jqLite.replaceWith, which can replace items which
* have no parents, provided that the containing jqLite collection is provided.
*
* @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
* in the root of the tree.
* @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
* the shell, but replace its DOM node reference.
* @param {Node} newNode The new DOM node.
*/
function replaceWith($rootElement, elementsToRemove, newNode) {
var firstElementToRemove = elementsToRemove[0],
removeCount = elementsToRemove.length,
parent = firstElementToRemove.parentNode,
i, ii;
if ($rootElement) {
for (i = 0, ii = $rootElement.length; i < ii; i++) {
if ($rootElement[i] == firstElementToRemove) {
$rootElement[i++] = newNode;
for (var j = i, j2 = j + removeCount - 1,
jj = $rootElement.length;
j < jj; j++, j2++) {
if (j2 < jj) {
$rootElement[j] = $rootElement[j2];
} else {
delete $rootElement[j];
}
}
$rootElement.length -= removeCount - 1;
// If the replaced element is also the jQuery .context then replace it
// .context is a deprecated jQuery api, so we should set it only when jQuery set it
// http://api.jquery.com/context/
if ($rootElement.context === firstElementToRemove) {
$rootElement.context = newNode;
}
break;
}
}
}
if (parent) {
parent.replaceChild(newNode, firstElementToRemove);
}
// TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
var fragment = document.createDocumentFragment();
fragment.appendChild(firstElementToRemove);
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
// data here because there's no public interface in jQuery to do that and copying over
// event listeners (which is the main use of private data) wouldn't work anyway.
jqLite(newNode).data(jqLite(firstElementToRemove).data());
// Remove data of the replaced element. We cannot just call .remove()
// on the element it since that would deallocate scope that is needed
// for the new node. Instead, remove the data "manually".
if (!jQuery) {
delete jqLite.cache[firstElementToRemove[jqLite.expando]];
} else {
// jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
// the replaced element. The cleanData version monkey-patched by Angular would cause
// the scope to be trashed and we do need the very same scope to work with the new
// element. However, we cannot just cache the non-patched version and use it here as
// that would break if another library patches the method after Angular does (one
// example is jQuery UI). Instead, set a flag indicating scope destroying should be
// skipped this one time.
skipDestroyOnNextJQueryCleanData = true;
jQuery.cleanData([firstElementToRemove]);
}
for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
var element = elementsToRemove[k];
jqLite(element).remove(); // must do this way to clean up expando
fragment.appendChild(element);
delete elementsToRemove[k];
}
elementsToRemove[0] = newNode;
elementsToRemove.length = 1;
}
function cloneAndAnnotateFn(fn, annotation) {
return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
}
function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
try {
linkFn(scope, $element, attrs, controllers, transcludeFn);
} catch (e) {
$exceptionHandler(e, startingTag($element));
}
}
// Set up $watches for isolate scope and controller bindings. This process
// only occurs for isolate scopes and new scopes with controllerAs.
function initializeDirectiveBindings(scope, attrs, destination, bindings,
directive, newScope) {
var onNewScopeDestroyed;
forEach(bindings, function(definition, scopeName) {
var attrName = definition.attrName,
optional = definition.optional,
mode = definition.mode, // @, =, or &
lastValue,
parentGet, parentSet, compare;
switch (mode) {
case '@':
attrs.$observe(attrName, function(value) {
destination[scopeName] = value;
});
attrs.$$observers[attrName].$$scope = scope;
if (attrs[attrName]) {
// If the attribute has been provided then we trigger an interpolation to ensure
// the value is there for use in the link fn
destination[scopeName] = $interpolate(attrs[attrName])(scope);
}
break;
case '=':
if (optional && !attrs[attrName]) {
return;
}
parentGet = $parse(attrs[attrName]);
if (parentGet.literal) {
compare = equals;
} else {
compare = function(a, b) { return a === b || (a !== a && b !== b); };
}
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
lastValue = destination[scopeName] = parentGet(scope);
throw $compileMinErr('nonassign',
"Expression '{0}' used with directive '{1}' is non-assignable!",
attrs[attrName], directive.name);
};
lastValue = destination[scopeName] = parentGet(scope);
var parentValueWatch = function parentValueWatch(parentValue) {
if (!compare(parentValue, destination[scopeName])) {
// we are out of sync and need to copy
if (!compare(parentValue, lastValue)) {
// parent changed and it has precedence
destination[scopeName] = parentValue;
} else {
// if the parent can be assigned then do so
parentSet(scope, parentValue = destination[scopeName]);
}
}
return lastValue = parentValue;
};
parentValueWatch.$stateful = true;
var unwatch;
if (definition.collection) {
unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
} else {
unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
}
onNewScopeDestroyed = (onNewScopeDestroyed || []);
onNewScopeDestroyed.push(unwatch);
break;
case '&':
// Don't assign Object.prototype method to scope
if (!attrs.hasOwnProperty(attrName) && optional) break;
parentGet = $parse(attrs[attrName]);
// Don't assign noop to destination if expression is not valid
if (parentGet === noop && optional) break;
destination[scopeName] = function(locals) {
return parentGet(scope, locals);
};
break;
}
});
var destroyBindings = onNewScopeDestroyed ? function destroyBindings() {
for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) {
onNewScopeDestroyed[i]();
}
} : noop;
if (newScope && destroyBindings !== noop) {
newScope.$on('$destroy', destroyBindings);
return noop;
}
return destroyBindings;
}
}];
}
var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
/**
* Converts all accepted directives format into proper directive name.
* @param name Name to normalize
*/
function directiveNormalize(name) {
return camelCase(name.replace(PREFIX_REGEXP, ''));
}
/**
* @ngdoc type
* @name $compile.directive.Attributes
*
* @description
* A shared object between directive compile / linking functions which contains normalized DOM
* element attributes. The values reflect current binding state `{{ }}`. The normalization is
* needed since all of these are treated as equivalent in Angular:
*
* ```
* <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
* ```
*/
/**
* @ngdoc property
* @name $compile.directive.Attributes#$attr
*
* @description
* A map of DOM element attribute names to the normalized name. This is
* needed to do reverse lookup from normalized name back to actual name.
*/
/**
* @ngdoc method
* @name $compile.directive.Attributes#$set
* @kind function
*
* @description
* Set DOM element attribute value.
*
*
* @param {string} name Normalized element attribute name of the property to modify. The name is
* reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
* property to the original name.
* @param {string} value Value to set the attribute to. The value can be an interpolated string.
*/
/**
* Closure compiler type information
*/
function nodesetLinkingFn(
/* angular.Scope */ scope,
/* NodeList */ nodeList,
/* Element */ rootElement,
/* function(Function) */ boundTranscludeFn
) {}
function directiveLinkingFn(
/* nodesetLinkingFn */ nodesetLinkingFn,
/* angular.Scope */ scope,
/* Node */ node,
/* Element */ rootElement,
/* function(Function) */ boundTranscludeFn
) {}
function tokenDifference(str1, str2) {
var values = '',
tokens1 = str1.split(/\s+/),
tokens2 = str2.split(/\s+/);
outer:
for (var i = 0; i < tokens1.length; i++) {
var token = tokens1[i];
for (var j = 0; j < tokens2.length; j++) {
if (token == tokens2[j]) continue outer;
}
values += (values.length > 0 ? ' ' : '') + token;
}
return values;
}
function removeComments(jqNodes) {
jqNodes = jqLite(jqNodes);
var i = jqNodes.length;
if (i <= 1) {
return jqNodes;
}
while (i--) {
var node = jqNodes[i];
if (node.nodeType === NODE_TYPE_COMMENT) {
splice.call(jqNodes, i, 1);
}
}
return jqNodes;
}
var $controllerMinErr = minErr('$controller');
var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
function identifierForController(controller, ident) {
if (ident && isString(ident)) return ident;
if (isString(controller)) {
var match = CNTRL_REG.exec(controller);
if (match) return match[3];
}
}
/**
* @ngdoc provider
* @name $controllerProvider
* @description
* The {@link ng.$controller $controller service} is used by Angular to create new
* controllers.
*
* This provider allows controller registration via the
* {@link ng.$controllerProvider#register register} method.
*/
function $ControllerProvider() {
var controllers = {},
globals = false;
/**
* @ngdoc method
* @name $controllerProvider#register
* @param {string|Object} name Controller name, or an object map of controllers where the keys are
* the names and the values are the constructors.
* @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
* annotations in the array notation).
*/
this.register = function(name, constructor) {
assertNotHasOwnProperty(name, 'controller');
if (isObject(name)) {
extend(controllers, name);
} else {
controllers[name] = constructor;
}
};
/**
* @ngdoc method
* @name $controllerProvider#allowGlobals
* @description If called, allows `$controller` to find controller constructors on `window`
*/
this.allowGlobals = function() {
globals = true;
};
this.$get = ['$injector', '$window', function($injector, $window) {
/**
* @ngdoc service
* @name $controller
* @requires $injector
*
* @param {Function|string} constructor If called with a function then it's considered to be the
* controller constructor function. Otherwise it's considered to be a string which is used
* to retrieve the controller constructor using the following steps:
*
* * check if a controller with given name is registered via `$controllerProvider`
* * check if evaluating the string on the current scope returns a constructor
* * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
* `window` object (not recommended)
*
* The string can use the `controller as property` syntax, where the controller instance is published
* as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
* to work correctly.
*
* @param {Object} locals Injection locals for Controller.
* @return {Object} Instance of given controller.
*
* @description
* `$controller` service is responsible for instantiating controllers.
*
* It's just a simple call to {@link auto.$injector $injector}, but extracted into
* a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
*/
return function(expression, locals, later, ident) {
// PRIVATE API:
// param `later` --- indicates that the controller's constructor is invoked at a later time.
// If true, $controller will allocate the object with the correct
// prototype chain, but will not invoke the controller until a returned
// callback is invoked.
// param `ident` --- An optional label which overrides the label parsed from the controller
// expression, if any.
var instance, match, constructor, identifier;
later = later === true;
if (ident && isString(ident)) {
identifier = ident;
}
if (isString(expression)) {
match = expression.match(CNTRL_REG);
if (!match) {
throw $controllerMinErr('ctrlfmt',
"Badly formed controller string '{0}'. " +
"Must match `__name__ as __id__` or `__name__`.", expression);
}
constructor = match[1],
identifier = identifier || match[3];
expression = controllers.hasOwnProperty(constructor)
? controllers[constructor]
: getter(locals.$scope, constructor, true) ||
(globals ? getter($window, constructor, true) : undefined);
assertArgFn(expression, constructor, true);
}
if (later) {
// Instantiate controller later:
// This machinery is used to create an instance of the object before calling the
// controller's constructor itself.
//
// This allows properties to be added to the controller before the constructor is
// invoked. Primarily, this is used for isolate scope bindings in $compile.
//
// This feature is not intended for use by applications, and is thus not documented
// publicly.
// Object creation: http://jsperf.com/create-constructor/2
var controllerPrototype = (isArray(expression) ?
expression[expression.length - 1] : expression).prototype;
instance = Object.create(controllerPrototype || null);
if (identifier) {
addIdentifier(locals, identifier, instance, constructor || expression.name);
}
var instantiate;
return instantiate = extend(function() {
var result = $injector.invoke(expression, instance, locals, constructor);
if (result !== instance && (isObject(result) || isFunction(result))) {
instance = result;
if (identifier) {
// If result changed, re-assign controllerAs value to scope.
addIdentifier(locals, identifier, instance, constructor || expression.name);
}
}
return instance;
}, {
instance: instance,
identifier: identifier
});
}
instance = $injector.instantiate(expression, locals, constructor);
if (identifier) {
addIdentifier(locals, identifier, instance, constructor || expression.name);
}
return instance;
};
function addIdentifier(locals, identifier, instance, name) {
if (!(locals && isObject(locals.$scope))) {
throw minErr('$controller')('noscp',
"Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
name, identifier);
}
locals.$scope[identifier] = instance;
}
}];
}
/**
* @ngdoc service
* @name $document
* @requires $window
*
* @description
* A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
*
* @example
<example module="documentExample">
<file name="index.html">
<div ng-controller="ExampleController">
<p>$document title: <b ng-bind="title"></b></p>
<p>window.document title: <b ng-bind="windowTitle"></b></p>
</div>
</file>
<file name="script.js">
angular.module('documentExample', [])
.controller('ExampleController', ['$scope', '$document', function($scope, $document) {
$scope.title = $document[0].title;
$scope.windowTitle = angular.element(window.document)[0].title;
}]);
</file>
</example>
*/
function $DocumentProvider() {
this.$get = ['$window', function(window) {
return jqLite(window.document);
}];
}
/**
* @ngdoc service
* @name $exceptionHandler
* @requires ng.$log
*
* @description
* Any uncaught exception in angular expressions is delegated to this service.
* The default implementation simply delegates to `$log.error` which logs it into
* the browser console.
*
* In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
* {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
*
* ## Example:
*
* ```js
* angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
* return function(exception, cause) {
* exception.message += ' (caused by "' + cause + '")';
* throw exception;
* };
* });
* ```
*
* This example will override the normal action of `$exceptionHandler`, to make angular
* exceptions fail hard when they happen, instead of just logging to the console.
*
* <hr />
* Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
* methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
* (unless executed during a digest).
*
* If you wish, you can manually delegate exceptions, e.g.
* `try { ... } catch(e) { $exceptionHandler(e); }`
*
* @param {Error} exception Exception associated with the error.
* @param {string=} cause optional information about the context in which
* the error was thrown.
*
*/
function $ExceptionHandlerProvider() {
this.$get = ['$log', function($log) {
return function(exception, cause) {
$log.error.apply($log, arguments);
};
}];
}
var APPLICATION_JSON = 'application/json';
var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
var JSON_START = /^\[|^\{(?!\{)/;
var JSON_ENDS = {
'[': /]$/,
'{': /}$/
};
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
function serializeValue(v) {
if (isObject(v)) {
return isDate(v) ? v.toISOString() : toJson(v);
}
return v;
}
function $HttpParamSerializerProvider() {
/**
* @ngdoc service
* @name $httpParamSerializer
* @description
*
* Default $http params serializer that converts objects to a part of a request URL
* according to the following rules:
* * `{'foo': 'bar'}` results in `foo=bar`
* * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
* * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
* * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
* */
this.$get = function() {
return function ngParamSerializer(params) {
if (!params) return '';
var parts = [];
forEachSorted(params, function(value, key) {
if (value === null || isUndefined(value)) return;
if (isArray(value)) {
forEach(value, function(v, k) {
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
});
} else {
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
}
});
return parts.join('&');
};
};
}
function $HttpParamSerializerJQLikeProvider() {
/**
* @ngdoc service
* @name $httpParamSerializerJQLike
* @description
*
* Alternative $http params serializer that follows jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
* */
this.$get = function() {
return function jQueryLikeParamSerializer(params) {
if (!params) return '';
var parts = [];
serialize(params, '', true);
return parts.join('&');
function serialize(toSerialize, prefix, topLevel) {
if (toSerialize === null || isUndefined(toSerialize)) return;
if (isArray(toSerialize)) {
forEach(toSerialize, function(value) {
serialize(value, prefix + '[]');
});
} else if (isObject(toSerialize) && !isDate(toSerialize)) {
forEachSorted(toSerialize, function(value, key) {
serialize(value, prefix +
(topLevel ? '' : '[') +
key +
(topLevel ? '' : ']'));
});
} else {
parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
}
}
};
};
}
function defaultHttpResponseTransform(data, headers) {
if (isString(data)) {
// Strip json vulnerability protection prefix and trim whitespace
var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
if (tempData) {
var contentType = headers('Content-Type');
if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
data = fromJson(tempData);
}
}
}
return data;
}
function isJsonLike(str) {
var jsonStart = str.match(JSON_START);
return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
}
/**
* Parse headers into key value object
*
* @param {string} headers Raw headers as a string
* @returns {Object} Parsed headers as key value object
*/
function parseHeaders(headers) {
var parsed = createMap(), i;
function fillInParsed(key, val) {
if (key) {
parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
}
}
if (isString(headers)) {
forEach(headers.split('\n'), function(line) {
i = line.indexOf(':');
fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
});
} else if (isObject(headers)) {
forEach(headers, function(headerVal, headerKey) {
fillInParsed(lowercase(headerKey), trim(headerVal));
});
}
return parsed;
}
/**
* Returns a function that provides access to parsed headers.
*
* Headers are lazy parsed when first requested.
* @see parseHeaders
*
* @param {(string|Object)} headers Headers to provide access to.
* @returns {function(string=)} Returns a getter function which if called with:
*
* - if called with single an argument returns a single header value or null
* - if called with no arguments returns an object containing all headers.
*/
function headersGetter(headers) {
var headersObj;
return function(name) {
if (!headersObj) headersObj = parseHeaders(headers);
if (name) {
var value = headersObj[lowercase(name)];
if (value === void 0) {
value = null;
}
return value;
}
return headersObj;
};
}
/**
* Chain all given functions
*
* This function is used for both request and response transforming
*
* @param {*} data Data to transform.
* @param {function(string=)} headers HTTP headers getter fn.
* @param {number} status HTTP status code of the response.
* @param {(Function|Array.<Function>)} fns Function or an array of functions.
* @returns {*} Transformed data.
*/
function transformData(data, headers, status, fns) {
if (isFunction(fns)) {
return fns(data, headers, status);
}
forEach(fns, function(fn) {
data = fn(data, headers, status);
});
return data;
}
function isSuccess(status) {
return 200 <= status && status < 300;
}
/**
* @ngdoc provider
* @name $httpProvider
* @description
* Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
* */
function $HttpProvider() {
/**
* @ngdoc property
* @name $httpProvider#defaults
* @description
*
* 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 `default.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.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
* Defaults value is `'XSRF-TOKEN'`.
*
* - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
* XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
*
* - **`defaults.headers`** - {Object} - Default headers for all $http requests.
* Refer to {@link ng.$http#setting-http-headers $http} for documentation on
* setting default headers.
* - **`defaults.headers.common`**
* - **`defaults.headers.post`**
* - **`defaults.headers.put`**
* - **`defaults.headers.patch`**
*
* - **`defaults.paramSerializer`** - {string|function(Object<string,string>):string} - A function used to prepare string representation
* of request parameters (specified as an object).
* If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
* Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
*
**/
var defaults = this.defaults = {
// transform incoming response data
transformResponse: [defaultHttpResponseTransform],
// transform outgoing request data
transformRequest: [function(d) {
return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
}],
// default headers
headers: {
common: {
'Accept': 'application/json, text/plain, */*'
},
post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
},
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
paramSerializer: '$httpParamSerializer'
};
var useApplyAsync = false;
/**
* @ngdoc method
* @name $httpProvider#useApplyAsync
* @description
*
* Configure $http service to combine processing of multiple http responses received at around
* the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
* significant performance improvement for bigger applications that make many HTTP requests
* concurrently (common during application bootstrap).
*
* Defaults to false. If no value is specified, returns the current configured value.
*
* @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
* "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
* to load and share the same digest cycle.
*
* @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
* otherwise, returns the current configured value.
**/
this.useApplyAsync = function(value) {
if (isDefined(value)) {
useApplyAsync = !!value;
return this;
}
return useApplyAsync;
};
/**
* @ngdoc property
* @name $httpProvider#interceptors
* @description
*
* Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
* pre-processing of request or postprocessing of responses.
*
* These service factories are ordered by request, i.e. they are applied in the same order as the
* array, on request, but reverse order, on response.
*
* {@link ng.$http#interceptors Interceptors detailed info}
**/
var interceptorFactories = this.interceptors = [];
this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
var defaultCache = $cacheFactory('$http');
/**
* Make sure that default param serializer is exposed as a function
*/
defaults.paramSerializer = isString(defaults.paramSerializer) ?
$injector.get(defaults.paramSerializer) : defaults.paramSerializer;
/**
* Interceptors stored in reverse order. Inner interceptors before outer interceptors.
* The reversal is needed so that we can build up the interception chain around the
* server request.
*/
var reversedInterceptors = [];
forEach(interceptorFactories, function(interceptorFactory) {
reversedInterceptors.unshift(isString(interceptorFactory)
? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
});
/**
* @ngdoc service
* @kind function
* @name $http
* @requires ng.$httpBackend
* @requires $cacheFactory
* @requires $rootScope
* @requires $q
* @requires $injector
*
* @description
* The `$http` service is a core Angular service that facilitates communication with the remote
* HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
* object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
*
* For unit testing applications that use `$http` service, see
* {@link ngMock.$httpBackend $httpBackend mock}.
*
* For a higher level of abstraction, please check out the {@link ngResource.$resource
* $resource} service.
*
* The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
* the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
* it is important to familiarize yourself with these APIs and the guarantees they provide.
*
*
* ## General usage
* The `$http` service is a function which takes a single argument — a configuration object —
* that is used to generate an HTTP request and returns a {@link ng.$q promise}
* with two $http specific methods: `success` and `error`.
*
* ```js
* // Simple GET request example :
* $http.get('/someUrl').
* success(function(data, status, headers, config) {
* // this callback will be called asynchronously
* // when the response is available
* }).
* error(function(data, status, headers, config) {
* // called asynchronously if an error occurs
* // or server returns response with an error status.
* });
* ```
*
* ```js
* // Simple POST request example (passing data) :
* $http.post('/someUrl', {msg:'hello word!'}).
* success(function(data, status, headers, config) {
* // this callback will be called asynchronously
* // when the response is available
* }).
* error(function(data, status, headers, config) {
* // called asynchronously if an error occurs
* // or server returns response with an error status.
* });
* ```
*
*
* Since the returned value of calling the $http function is a `promise`, you can also use
* the `then` method to register callbacks, and these callbacks will receive a single argument –
* an object representing the response. See the API signature and type info below for more
* details.
*
* A response status code between 200 and 299 is considered a success status and
* will result in the success callback being called. Note that if the response is a redirect,
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
* called for such responses.
*
* ## Writing Unit Tests that use $http
* When unit testing (using {@link ngMock ngMock}), it is necessary to call
* {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
* request using trained responses.
*
* ```
* $httpBackend.expectGET(...);
* $http.get(...);
* $httpBackend.flush();
* ```
*
* ## Shortcut methods
*
* Shortcut methods are also available. All shortcut methods require passing in the URL, and
* request data must be passed in for POST/PUT requests.
*
* ```js
* $http.get('/someUrl').success(successCallback);
* $http.post('/someUrl', data).success(successCallback);
* ```
*
* Complete list of shortcut methods:
*
* - {@link ng.$http#get $http.get}
* - {@link ng.$http#head $http.head}
* - {@link ng.$http#post $http.post}
* - {@link ng.$http#put $http.put}
* - {@link ng.$http#delete $http.delete}
* - {@link ng.$http#jsonp $http.jsonp}
* - {@link ng.$http#patch $http.patch}
*
*
* ## Setting HTTP Headers
*
* The $http service will automatically add certain HTTP headers to all requests. These defaults
* can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
* object, which currently contains this default configuration:
*
* - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
* - `Accept: application/json, text/plain, * / *`
* - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
* - `Content-Type: application/json`
* - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
* - `Content-Type: application/json`
*
* To add or overwrite these defaults, simply add or remove a property from these configuration
* objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
* with the lowercased HTTP method name as the key, e.g.
* `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
*
* The defaults can also be set at runtime via the `$http.defaults` object in the same
* fashion. For example:
*
* ```
* module.run(function($http) {
* $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
* });
* ```
*
* In addition, you can supply a `headers` property in the config object passed when
* calling `$http(config)`, which overrides the defaults without changing them globally.
*
* To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
* Use the `headers` property, setting the desired header to `undefined`. For example:
*
* ```js
* var req = {
* method: 'POST',
* url: 'http://example.com',
* headers: {
* 'Content-Type': undefined
* },
* data: { test: 'test' }
* }
*
* $http(req).success(function(){...}).error(function(){...});
* ```
*
* ## Transforming Requests and Responses
*
* Both requests and responses can be transformed using transformation functions: `transformRequest`
* and `transformResponse`. These properties can be a single function that returns
* 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.
*
* ### Default Transformations
*
* The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
* `defaults.transformResponse` properties. If a request does not provide its own transformations
* then these will be applied.
*
* You can augment or replace the default transformations by modifying these properties by adding to or
* replacing the array.
*
* Angular provides the following default transformations:
*
* Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
*
* - If the `data` property of the request configuration object contains an object, serialize it
* into JSON format.
*
* Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
*
* - If XSRF prefix is detected, strip it (see Security Considerations section below).
* - If JSON response is detected, deserialize it using a JSON parser.
*
*
* ### Overriding the Default Transformations Per Request
*
* If you wish override the request/response transformations only for a single request then provide
* `transformRequest` and/or `transformResponse` properties on the configuration object passed
* into `$http`.
*
* Note that if you provide these properties on the config object the default transformations will be
* overwritten. If you wish to augment the default transformations then you must include them in your
* local transformation array.
*
* The following code demonstrates adding a new response transformation to be run after the default response
* transformations have been run.
*
* ```js
* function appendTransform(defaults, transform) {
*
* // We can't guarantee that the default transformation is an array
* defaults = angular.isArray(defaults) ? defaults : [defaults];
*
* // Append the new transformation to the defaults
* return defaults.concat(transform);
* }
*
* $http({
* url: '...',
* method: 'GET',
* transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
* return doTransform(value);
* })
* });
* ```
*
*
* ## 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.
*
* Note that even if the response is served from cache, delivery of the data is asynchronous in
* the same way that real requests are.
*
* 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.
*
* 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.
*
* If you set the default cache to `false` then only requests that specify their own custom
* cache object will be cached.
*
* ## Interceptors
*
* Before you start creating interceptors, be sure to understand the
* {@link ng.$q $q and deferred/promise APIs}.
*
* For purposes of global error handling, authentication, or any kind of synchronous or
* asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
* able to intercept requests before they are handed to the server and
* responses before they are handed over to the application code that
* initiated these requests. The interceptors leverage the {@link ng.$q
* promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
*
* The interceptors are service factories that are registered with the `$httpProvider` by
* adding them to the `$httpProvider.interceptors` array. The factory is called and
* injected with dependencies (if specified) and returns the interceptor.
*
* There are two kinds of interceptors (and two kinds of rejection interceptors):
*
* * `request`: interceptors get called with a http `config` object. The function is free to
* modify the `config` object or create a new one. The function needs to return the `config`
* object directly, or a promise containing the `config` or a new `config` object.
* * `requestError`: interceptor gets called when a previous interceptor threw an error or
* resolved with a rejection.
* * `response`: interceptors get called with http `response` object. The function is free to
* modify the `response` object or create a new one. The function needs to return the `response`
* object directly, or as a promise containing the `response` or a new `response` object.
* * `responseError`: interceptor gets called when a previous interceptor threw an error or
* resolved with a rejection.
*
*
* ```js
* // register the interceptor as a service
* $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
* return {
* // optional method
* 'request': function(config) {
* // do something on success
* return config;
* },
*
* // optional method
* 'requestError': function(rejection) {
* // do something on error
* if (canRecover(rejection)) {
* return responseOrNewPromise
* }
* return $q.reject(rejection);
* },
*
*
*
* // optional method
* 'response': function(response) {
* // do something on success
* return response;
* },
*
* // optional method
* 'responseError': function(rejection) {
* // do something on error
* if (canRecover(rejection)) {
* return responseOrNewPromise
* }
* return $q.reject(rejection);
* }
* };
* });
*
* $httpProvider.interceptors.push('myHttpInterceptor');
*
*
* // alternatively, register the interceptor via an anonymous factory
* $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
* return {
* 'request': function(config) {
* // same as above
* },
*
* 'response': function(response) {
* // same as above
* }
* };
* });
* ```
*
* ## Security Considerations
*
* When designing web applications, consider security threats from:
*
* - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
* - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
*
* Both server and the client must cooperate in order to eliminate these threats. Angular comes
* pre-configured with strategies that address these issues, but for this to work backend server
* cooperation is required.
*
* ### JSON Vulnerability Protection
*
* A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
* allows third party website to turn your JSON resource URL into
* [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
* counter this your server can prefix all JSON requests with following string `")]}',\n"`.
* Angular will automatically strip the prefix before processing it as JSON.
*
* For example if your server needs to return:
* ```js
* ['one','two']
* ```
*
* which is vulnerable to attack, your server can return:
* ```js
* )]}',
* ['one','two']
* ```
*
* Angular will strip the prefix, before processing the JSON.
*
*
* ### Cross Site Request Forgery (XSRF) Protection
*
* [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
* an unauthorized site can gain your user's private data. Angular provides a mechanism
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
* (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
* JavaScript that runs on your domain could read the cookie, your server can be assured that
* the XHR came from JavaScript running on your domain. The header will not be set for
* cross-domain requests.
*
* To take advantage of this, your server needs to set a token in a JavaScript readable session
* cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
* server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
* that only JavaScript running on your domain could have sent the request. The token must be
* unique for each user and must be verifiable by the server (to prevent the JavaScript from
* making up its own tokens). We recommend that the token is a digest of your site's
* authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
* for added security.
*
* The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
* properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
* or the per-request config object.
*
*
* @param {object} config Object describing the request to be made and how it should be
* processed. The object has following properties:
*
* - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
* - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
* - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
* to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
* JSONified.
* - **data** – `{string|Object}` – Data to be sent as the request message data.
* - **headers** – `{Object}` – Map of strings or functions which return strings representing
* HTTP headers to send to the server. If the return value of a function is null, the
* header will not be sent. Functions accept a config object as an argument.
* - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
* - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
* - **transformRequest** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
* transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version.
* See {@link ng.$http#overriding-the-default-transformations-per-request
* Overriding the Default Transformations}
* - **transformResponse** –
* `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
* 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 Transformations}
* - **paramSerializer** - {string|function(Object<string,string>):string} - A function used to prepare string representation
* of request parameters (specified as an object).
* Is specified as string, it is interpreted as function registered in with the {$injector}.
* - **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.
* - **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
* XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
* for more information.
* - **responseType** - `{string}` - see
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
*
* @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
* standard `then` method and two http specific methods: `success` and `error`. The `then`
* method takes two arguments a success and an error callback which will be called with a
* response object. The `success` and `error` methods take a single argument - a function that
* will be called when the request succeeds or fails respectively. The arguments passed into
* these functions are destructured representation of the response object passed into the
* `then` method. The response object has these properties:
*
* - **data** – `{string|Object}` – The response body transformed with the transform
* functions.
* - **status** – `{number}` – HTTP status code of the response.
* - **headers** – `{function([headerName])}` – Header getter function.
* - **config** – `{Object}` – The configuration object that was used to generate the request.
* - **statusText** – `{string}` – HTTP status text of the response.
*
* @property {Array.<Object>} pendingRequests Array of config objects for currently pending
* requests. This is primarily meant to be used for debugging purposes.
*
*
* @example
<example module="httpExample">
<file name="index.html">
<div ng-controller="FetchController">
<select ng-model="method" aria-label="Request method">
<option>GET</option>
<option>JSONP</option>
</select>
<input type="text" ng-model="url" size="80" aria-label="URL" />
<button id="fetchbtn" ng-click="fetch()">fetch</button><br>
<button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
<button id="samplejsonpbtn"
ng-click="updateModel('JSONP',
'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
Sample JSONP
</button>
<button id="invalidjsonpbtn"
ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
Invalid JSONP
</button>
<pre>http status code: {{status}}</pre>
<pre>http response data: {{data}}</pre>
</div>
</file>
<file name="script.js">
angular.module('httpExample', [])
.controller('FetchController', ['$scope', '$http', '$templateCache',
function($scope, $http, $templateCache) {
$scope.method = 'GET';
$scope.url = 'http-hello.html';
$scope.fetch = function() {
$scope.code = null;
$scope.response = null;
$http({method: $scope.method, url: $scope.url, cache: $templateCache}).
success(function(data, status) {
$scope.status = status;
$scope.data = data;
}).
error(function(data, status) {
$scope.data = data || "Request failed";
$scope.status = status;
});
};
$scope.updateModel = function(method, url) {
$scope.method = method;
$scope.url = url;
};
}]);
</file>
<file name="http-hello.html">
Hello, $http!
</file>
<file name="protractor.js" type="protractor">
var status = element(by.binding('status'));
var data = element(by.binding('data'));
var fetchBtn = element(by.id('fetchbtn'));
var sampleGetBtn = element(by.id('samplegetbtn'));
var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
it('should make an xhr GET request', function() {
sampleGetBtn.click();
fetchBtn.click();
expect(status.getText()).toMatch('200');
expect(data.getText()).toMatch(/Hello, \$http!/);
});
// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
// it('should make a JSONP request to angularjs.org', function() {
// sampleJsonpBtn.click();
// fetchBtn.click();
// expect(status.getText()).toMatch('200');
// expect(data.getText()).toMatch(/Super Hero!/);
// });
it('should make JSONP request to invalid URL and invoke the error handler',
function() {
invalidJsonpBtn.click();
fetchBtn.click();
expect(status.getText()).toMatch('0');
expect(data.getText()).toMatch('Request failed');
});
</file>
</example>
*/
function $http(requestConfig) {
if (!angular.isObject(requestConfig)) {
throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
}
var config = extend({
method: 'get',
transformRequest: defaults.transformRequest,
transformResponse: defaults.transformResponse,
paramSerializer: defaults.paramSerializer
}, requestConfig);
config.headers = mergeHeaders(requestConfig);
config.method = uppercase(config.method);
config.paramSerializer = isString(config.paramSerializer) ?
$injector.get(config.paramSerializer) : config.paramSerializer;
var serverRequest = function(config) {
var headers = config.headers;
var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
// strip content-type if data is undefined
if (isUndefined(reqData)) {
forEach(headers, function(value, header) {
if (lowercase(header) === 'content-type') {
delete headers[header];
}
});
}
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
config.withCredentials = defaults.withCredentials;
}
// send request
return sendReq(config, reqData).then(transformResponse, transformResponse);
};
var chain = [serverRequest, undefined];
var promise = $q.when(config);
// apply interceptors
forEach(reversedInterceptors, function(interceptor) {
if (interceptor.request || interceptor.requestError) {
chain.unshift(interceptor.request, interceptor.requestError);
}
if (interceptor.response || interceptor.responseError) {
chain.push(interceptor.response, interceptor.responseError);
}
});
while (chain.length) {
var thenFn = chain.shift();
var rejectFn = chain.shift();
promise = promise.then(thenFn, rejectFn);
}
promise.success = function(fn) {
assertArgFn(fn, 'fn');
promise.then(function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
};
promise.error = function(fn) {
assertArgFn(fn, 'fn');
promise.then(null, function(response) {
fn(response.data, response.status, response.headers, config);
});
return promise;
};
return promise;
function transformResponse(response) {
// make a copy since the response must be cacheable
var resp = extend({}, response);
if (!response.data) {
resp.data = response.data;
} else {
resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
}
return (isSuccess(response.status))
? resp
: $q.reject(resp);
}
function executeHeaderFns(headers, config) {
var headerContent, processedHeaders = {};
forEach(headers, function(headerFn, header) {
if (isFunction(headerFn)) {
headerContent = headerFn(config);
if (headerContent != null) {
processedHeaders[header] = headerContent;
}
} else {
processedHeaders[header] = headerFn;
}
});
return processedHeaders;
}
function mergeHeaders(config) {
var defHeaders = defaults.headers,
reqHeaders = extend({}, config.headers),
defHeaderName, lowercaseDefHeaderName, reqHeaderName;
defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
// using for-in instead of forEach to avoid unecessary iteration after header has been found
defaultHeadersIteration:
for (defHeaderName in defHeaders) {
lowercaseDefHeaderName = lowercase(defHeaderName);
for (reqHeaderName in reqHeaders) {
if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
continue defaultHeadersIteration;
}
}
reqHeaders[defHeaderName] = defHeaders[defHeaderName];
}
// execute if header value is a function for merged headers
return executeHeaderFns(reqHeaders, shallowCopy(config));
}
}
$http.pendingRequests = [];
/**
* @ngdoc method
* @name $http#get
*
* @description
* Shortcut method to perform `GET` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
/**
* @ngdoc method
* @name $http#delete
*
* @description
* Shortcut method to perform `DELETE` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
/**
* @ngdoc method
* @name $http#head
*
* @description
* Shortcut method to perform `HEAD` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
/**
* @ngdoc method
* @name $http#jsonp
*
* @description
* Shortcut method to perform `JSONP` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request.
* The name of the callback should be the string `JSON_CALLBACK`.
* @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
createShortMethods('get', 'delete', 'head', 'jsonp');
/**
* @ngdoc method
* @name $http#post
*
* @description
* Shortcut method to perform `POST` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
* @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
/**
* @ngdoc method
* @name $http#put
*
* @description
* Shortcut method to perform `PUT` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
* @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
/**
* @ngdoc method
* @name $http#patch
*
* @description
* Shortcut method to perform `PATCH` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
* @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
createShortMethodsWithData('post', 'put', 'patch');
/**
* @ngdoc property
* @name $http#defaults
*
* @description
* Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
* default headers, withCredentials as well as request and response transformations.
*
* See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
*/
$http.defaults = defaults;
return $http;
function createShortMethods(names) {
forEach(arguments, function(name) {
$http[name] = function(url, config) {
return $http(extend({}, config || {}, {
method: name,
url: url
}));
};
});
}
function createShortMethodsWithData(name) {
forEach(arguments, function(name) {
$http[name] = function(url, data, config) {
return $http(extend({}, config || {}, {
method: name,
url: url,
data: data
}));
};
});
}
/**
* Makes the request.
*
* !!! ACCESSES CLOSURE VARS:
* $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
*/
function sendReq(config, reqData) {
var deferred = $q.defer(),
promise = deferred.promise,
cache,
cachedResp,
reqHeaders = config.headers,
url = buildUrl(config.url, config.paramSerializer(config.params));
$http.pendingRequests.push(config);
promise.then(removePendingReq, removePendingReq);
if ((config.cache || defaults.cache) && config.cache !== false &&
(config.method === 'GET' || config.method === 'JSONP')) {
cache = isObject(config.cache) ? config.cache
: isObject(defaults.cache) ? defaults.cache
: defaultCache;
}
if (cache) {
cachedResp = cache.get(url);
if (isDefined(cachedResp)) {
if (isPromiseLike(cachedResp)) {
// cached request has already been sent, but there is no response yet
cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
} else {
// serving from cache
if (isArray(cachedResp)) {
resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
} else {
resolvePromise(cachedResp, 200, {}, 'OK');
}
}
} else {
// put the promise for the non-transformed response into cache as a placeholder
cache.put(url, promise);
}
}
// if we won't have the response in cache, set the xsrf headers and
// send the request to the backend
if (isUndefined(cachedResp)) {
var xsrfValue = urlIsSameOrigin(config.url)
? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
: undefined;
if (xsrfValue) {
reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
}
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
config.withCredentials, config.responseType);
}
return promise;
/**
* Callback registered to $httpBackend():
* - caches the response if desired
* - resolves the raw $http promise
* - calls $apply
*/
function done(status, response, headersString, statusText) {
if (cache) {
if (isSuccess(status)) {
cache.put(url, [status, response, parseHeaders(headersString), statusText]);
} else {
// remove promise from the cache
cache.remove(url);
}
}
function resolveHttpPromise() {
resolvePromise(response, status, headersString, statusText);
}
if (useApplyAsync) {
$rootScope.$applyAsync(resolveHttpPromise);
} else {
resolveHttpPromise();
if (!$rootScope.$$phase) $rootScope.$apply();
}
}
/**
* Resolves the raw $http promise.
*/
function resolvePromise(response, status, headers, statusText) {
// normalize internal statuses to 0
status = Math.max(status, 0);
(isSuccess(status) ? deferred.resolve : deferred.reject)({
data: response,
status: status,
headers: headersGetter(headers),
config: config,
statusText: statusText
});
}
function resolvePromiseWithResult(result) {
resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
}
function removePendingReq() {
var idx = $http.pendingRequests.indexOf(config);
if (idx !== -1) $http.pendingRequests.splice(idx, 1);
}
}
function buildUrl(url, serializedParams) {
if (serializedParams.length > 0) {
url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
}
return url;
}
}];
}
function createXhr() {
return new window.XMLHttpRequest();
}
/**
* @ngdoc service
* @name $httpBackend
* @requires $window
* @requires $document
*
* @description
* HTTP backend used by the {@link ng.$http service} that delegates to
* XMLHttpRequest object or JSONP and deals with browser incompatibilities.
*
* You should never need to use this service directly, instead use the higher-level abstractions:
* {@link ng.$http $http} or {@link ngResource.$resource $resource}.
*
* During testing this implementation is swapped with {@link ngMock.$httpBackend mock
* $httpBackend} which can be trained with responses.
*/
function $HttpBackendProvider() {
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
}];
}
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
// TODO(vojta): fix the signature
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
$browser.$$incOutstandingRequestCount();
url = url || $browser.url();
if (lowercase(method) == 'jsonp') {
var callbackId = '_' + (callbacks.counter++).toString(36);
callbacks[callbackId] = function(data) {
callbacks[callbackId].data = data;
callbacks[callbackId].called = true;
};
var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
callbackId, function(status, text) {
completeRequest(callback, status, callbacks[callbackId].data, "", text);
callbacks[callbackId] = noop;
});
} else {
var xhr = createXhr();
xhr.open(method, url, true);
forEach(headers, function(value, key) {
if (isDefined(value)) {
xhr.setRequestHeader(key, value);
}
});
xhr.onload = function requestLoaded() {
var statusText = xhr.statusText || '';
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
var response = ('response' in xhr) ? xhr.response : xhr.responseText;
// normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
var status = xhr.status === 1223 ? 204 : xhr.status;
// fix status code when it is 0 (0 status is undocumented).
// Occurs when accessing file resources or on Android 4.1 stock browser
// while retrieving files from application cache.
if (status === 0) {
status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
}
completeRequest(callback,
status,
response,
xhr.getAllResponseHeaders(),
statusText);
};
var requestError = function() {
// The response is always empty
// See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
completeRequest(callback, -1, null, null, '');
};
xhr.onerror = requestError;
xhr.onabort = requestError;
if (withCredentials) {
xhr.withCredentials = true;
}
if (responseType) {
try {
xhr.responseType = responseType;
} catch (e) {
// WebKit added support for the json responseType value on 09/03/2013
// https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
// known to throw when setting the value "json" as the response type. Other older
// browsers implementing the responseType
//
// The json response type can be ignored if not supported, because JSON payloads are
// parsed on the client-side regardless.
if (responseType !== 'json') {
throw e;
}
}
}
xhr.send(post);
}
if (timeout > 0) {
var timeoutId = $browserDefer(timeoutRequest, timeout);
} else if (isPromiseLike(timeout)) {
timeout.then(timeoutRequest);
}
function timeoutRequest() {
jsonpDone && jsonpDone();
xhr && xhr.abort();
}
function completeRequest(callback, status, response, headersString, statusText) {
// cancel timeout and subsequent timeout promise resolution
if (timeoutId !== undefined) {
$browserDefer.cancel(timeoutId);
}
jsonpDone = xhr = null;
callback(status, response, headersString, statusText);
$browser.$$completeOutstandingRequest(noop);
}
};
function jsonpReq(url, callbackId, done) {
// we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
// - fetches local scripts via XHR and evals them
// - adds and immediately removes script elements from the document
var script = rawDocument.createElement('script'), callback = null;
script.type = "text/javascript";
script.src = url;
script.async = true;
callback = function(event) {
removeEventListenerFn(script, "load", callback);
removeEventListenerFn(script, "error", callback);
rawDocument.body.removeChild(script);
script = null;
var status = -1;
var text = "unknown";
if (event) {
if (event.type === "load" && !callbacks[callbackId].called) {
event = { type: "error" };
}
text = event.type;
status = event.type === "error" ? 404 : 200;
}
if (done) {
done(status, text);
}
};
addEventListenerFn(script, "load", callback);
addEventListenerFn(script, "error", callback);
rawDocument.body.appendChild(script);
return callback;
}
}
var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
$interpolateMinErr.throwNoconcat = function(text) {
throw $interpolateMinErr('noconcat',
"Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
"interpolations that concatenate multiple expressions when a trusted value is " +
"required. See http://docs.angularjs.org/api/ng.$sce", text);
};
$interpolateMinErr.interr = function(text, err) {
return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
};
/**
* @ngdoc provider
* @name $interpolateProvider
*
* @description
*
* Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
*
* @example
<example module="customInterpolationApp">
<file name="index.html">
<script>
var customInterpolationApp = angular.module('customInterpolationApp', []);
customInterpolationApp.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('//');
$interpolateProvider.endSymbol('//');
});
customInterpolationApp.controller('DemoController', function() {
this.label = "This binding is brought you by // interpolation symbols.";
});
</script>
<div ng-app="App" ng-controller="DemoController as demo">
//demo.label//
</div>
</file>
<file name="protractor.js" type="protractor">
it('should interpolate binding with custom symbols', function() {
expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
});
</file>
</example>
*/
function $InterpolateProvider() {
var startSymbol = '{{';
var endSymbol = '}}';
/**
* @ngdoc method
* @name $interpolateProvider#startSymbol
* @description
* Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
*
* @param {string=} value new value to set the starting symbol to.
* @returns {string|self} Returns the symbol when used as getter and self if used as setter.
*/
this.startSymbol = function(value) {
if (value) {
startSymbol = value;
return this;
} else {
return startSymbol;
}
};
/**
* @ngdoc method
* @name $interpolateProvider#endSymbol
* @description
* Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
*
* @param {string=} value new value to set the ending symbol to.
* @returns {string|self} Returns the symbol when used as getter and self if used as setter.
*/
this.endSymbol = function(value) {
if (value) {
endSymbol = value;
return this;
} else {
return endSymbol;
}
};
this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
var startSymbolLength = startSymbol.length,
endSymbolLength = endSymbol.length,
escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
function escape(ch) {
return '\\\\\\' + ch;
}
function unescapeText(text) {
return text.replace(escapedStartRegexp, startSymbol).
replace(escapedEndRegexp, endSymbol);
}
function stringify(value) {
if (value == null) { // null || undefined
return '';
}
switch (typeof value) {
case 'string':
break;
case 'number':
value = '' + value;
break;
default:
value = toJson(value);
}
return value;
}
/**
* @ngdoc service
* @name $interpolate
* @kind function
*
* @requires $parse
* @requires $sce
*
* @description
*
* Compiles a string with markup into an interpolation function. This service is used by the
* HTML {@link ng.$compile $compile} service for data binding. See
* {@link ng.$interpolateProvider $interpolateProvider} for configuring the
* interpolation markup.
*
*
* ```js
* var $interpolate = ...; // injected
* var exp = $interpolate('Hello {{name | uppercase}}!');
* expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
* ```
*
* `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
* `true`, the interpolation function will return `undefined` unless all embedded expressions
* evaluate to a value other than `undefined`.
*
* ```js
* var $interpolate = ...; // injected
* var context = {greeting: 'Hello', name: undefined };
*
* // default "forgiving" mode
* var exp = $interpolate('{{greeting}} {{name}}!');
* expect(exp(context)).toEqual('Hello !');
*
* // "allOrNothing" mode
* exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
* expect(exp(context)).toBeUndefined();
* context.name = 'Angular';
* expect(exp(context)).toEqual('Hello Angular!');
* ```
*
* `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
*
* ####Escaped Interpolation
* $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
* can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
* It will be rendered as a regular start/end marker, and will not be interpreted as an expression
* or binding.
*
* This enables web-servers to prevent script injection attacks and defacing attacks, to some
* degree, while also enabling code examples to work without relying on the
* {@link ng.directive:ngNonBindable ngNonBindable} directive.
*
* **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
* replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
* interpolation start/end markers with their escaped counterparts.**
*
* Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
* output when the $interpolate service processes the text. So, for HTML elements interpolated
* by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
* set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
* this is typically useful only when user-data is used in rendering a template from the server, or
* when otherwise untrusted data is used by a directive.
*
* <example>
* <file name="index.html">
* <div ng-init="username='A user'">
* <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
* </p>
* <p><strong>{{username}}</strong> attempts to inject code which will deface the
* application, but fails to accomplish their task, because the server has correctly
* escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
* characters.</p>
* <p>Instead, the result of the attempted script injection is visible, and can be removed
* from the database by an administrator.</p>
* </div>
* </file>
* </example>
*
* @param {string} text The text with markup to interpolate.
* @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
* embedded expression in order to return an interpolation function. Strings with no
* embedded expression will return null for the interpolation function.
* @param {string=} trustedContext when provided, the returned function passes the interpolated
* result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
* trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
* provides Strict Contextual Escaping for details.
* @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
* unless all embedded expressions evaluate to a value other than `undefined`.
* @returns {function(context)} an interpolation function which is used to compute the
* interpolated string. The function has these parameters:
*
* - `context`: evaluation context for all expressions embedded in the interpolated text
*/
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
allOrNothing = !!allOrNothing;
var startIndex,
endIndex,
index = 0,
expressions = [],
parseFns = [],
textLength = text.length,
exp,
concat = [],
expressionPositions = [];
while (index < textLength) {
if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
if (index !== startIndex) {
concat.push(unescapeText(text.substring(index, startIndex)));
}
exp = text.substring(startIndex + startSymbolLength, endIndex);
expressions.push(exp);
parseFns.push($parse(exp, parseStringifyInterceptor));
index = endIndex + endSymbolLength;
expressionPositions.push(concat.length);
concat.push('');
} else {
// we did not find an interpolation, so we have to add the remainder to the separators array
if (index !== textLength) {
concat.push(unescapeText(text.substring(index)));
}
break;
}
}
// Concatenating expressions makes it hard to reason about whether some combination of
// concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
// single expression be used for iframe[src], object[src], etc., we ensure that the value
// that's used is assigned or constructed by some JS code somewhere that is more testable or
// make it obvious that you bound the value to some user controlled value. This helps reduce
// the load when auditing for XSS issues.
if (trustedContext && concat.length > 1) {
$interpolateMinErr.throwNoconcat(text);
}
if (!mustHaveExpression || expressions.length) {
var compute = function(values) {
for (var i = 0, ii = expressions.length; i < ii; i++) {
if (allOrNothing && isUndefined(values[i])) return;
concat[expressionPositions[i]] = values[i];
}
return concat.join('');
};
var getValue = function(value) {
return trustedContext ?
$sce.getTrusted(trustedContext, value) :
$sce.valueOf(value);
};
return extend(function interpolationFn(context) {
var i = 0;
var ii = expressions.length;
var values = new Array(ii);
try {
for (; i < ii; i++) {
values[i] = parseFns[i](context);
}
return compute(values);
} catch (err) {
$exceptionHandler($interpolateMinErr.interr(text, err));
}
}, {
// all of these properties are undocumented for now
exp: text, //just for compatibility with regular watchers created via $watch
expressions: expressions,
$$watchDelegate: function(scope, listener) {
var lastValue;
return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
var currValue = compute(values);
if (isFunction(listener)) {
listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
}
lastValue = currValue;
});
}
});
}
function parseStringifyInterceptor(value) {
try {
value = getValue(value);
return allOrNothing && !isDefined(value) ? value : stringify(value);
} catch (err) {
$exceptionHandler($interpolateMinErr.interr(text, err));
}
}
}
/**
* @ngdoc method
* @name $interpolate#startSymbol
* @description
* Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
*
* Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
* the symbol.
*
* @returns {string} start symbol.
*/
$interpolate.startSymbol = function() {
return startSymbol;
};
/**
* @ngdoc method
* @name $interpolate#endSymbol
* @description
* Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
*
* Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
* the symbol.
*
* @returns {string} end symbol.
*/
$interpolate.endSymbol = function() {
return endSymbol;
};
return $interpolate;
}];
}
function $IntervalProvider() {
this.$get = ['$rootScope', '$window', '$q', '$$q',
function($rootScope, $window, $q, $$q) {
var intervals = {};
/**
* @ngdoc service
* @name $interval
*
* @description
* Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
* milliseconds.
*
* The return value of registering an interval function is a promise. This promise will be
* notified upon each tick of the interval, and will be resolved after `count` iterations, or
* run indefinitely if `count` is not defined. The value of the notification will be the
* number of iterations that have run.
* To cancel an interval, call `$interval.cancel(promise)`.
*
* In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
* move forward by `millis` milliseconds and trigger any functions scheduled to run in that
* time.
*
* <div class="alert alert-warning">
* **Note**: Intervals created by this service must be explicitly destroyed when you are finished
* with them. In particular they are not automatically destroyed when a controller's scope or a
* directive's element are destroyed.
* You should take this into consideration and make sure to always cancel the interval at the
* appropriate moment. See the example below for more details on how and when to do this.
* </div>
*
* @param {function()} fn A function that should be called repeatedly.
* @param {number} delay Number of milliseconds between each function call.
* @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
* indefinitely.
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
* @param {...*=} Pass additional parameters to the executed function.
* @returns {promise} A promise which will be notified on each iteration.
*
* @example
* <example module="intervalExample">
* <file name="index.html">
* <script>
* angular.module('intervalExample', [])
* .controller('ExampleController', ['$scope', '$interval',
* function($scope, $interval) {
* $scope.format = 'M/d/yy h:mm:ss a';
* $scope.blood_1 = 100;
* $scope.blood_2 = 120;
*
* var stop;
* $scope.fight = function() {
* // Don't start a new fight if we are already fighting
* if ( angular.isDefined(stop) ) return;
*
* stop = $interval(function() {
* if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
* $scope.blood_1 = $scope.blood_1 - 3;
* $scope.blood_2 = $scope.blood_2 - 4;
* } else {
* $scope.stopFight();
* }
* }, 100);
* };
*
* $scope.stopFight = function() {
* if (angular.isDefined(stop)) {
* $interval.cancel(stop);
* stop = undefined;
* }
* };
*
* $scope.resetFight = function() {
* $scope.blood_1 = 100;
* $scope.blood_2 = 120;
* };
*
* $scope.$on('$destroy', function() {
* // Make sure that the interval is destroyed too
* $scope.stopFight();
* });
* }])
* // Register the 'myCurrentTime' directive factory method.
* // We inject $interval and dateFilter service since the factory method is DI.
* .directive('myCurrentTime', ['$interval', 'dateFilter',
* function($interval, dateFilter) {
* // return the directive link function. (compile function not needed)
* return function(scope, element, attrs) {
* var format, // date format
* stopTime; // so that we can cancel the time updates
*
* // used to update the UI
* function updateTime() {
* element.text(dateFilter(new Date(), format));
* }
*
* // watch the expression, and update the UI on change.
* scope.$watch(attrs.myCurrentTime, function(value) {
* format = value;
* updateTime();
* });
*
* stopTime = $interval(updateTime, 1000);
*
* // listen on DOM destroy (removal) event, and cancel the next UI update
* // to prevent updating time after the DOM element was removed.
* element.on('$destroy', function() {
* $interval.cancel(stopTime);
* });
* }
* }]);
* </script>
*
* <div>
* <div ng-controller="ExampleController">
* <label>Date format: <input ng-model="format"></label> <hr/>
* Current time is: <span my-current-time="format"></span>
* <hr/>
* Blood 1 : <font color='red'>{{blood_1}}</font>
* Blood 2 : <font color='red'>{{blood_2}}</font>
* <button type="button" data-ng-click="fight()">Fight</button>
* <button type="button" data-ng-click="stopFight()">StopFight</button>
* <button type="button" data-ng-click="resetFight()">resetFight</button>
* </div>
* </div>
*
* </file>
* </example>
*/
function interval(fn, delay, count, invokeApply) {
var hasParams = arguments.length > 4,
args = hasParams ? sliceArgs(arguments, 4) : [],
setInterval = $window.setInterval,
clearInterval = $window.clearInterval,
iteration = 0,
skipApply = (isDefined(invokeApply) && !invokeApply),
deferred = (skipApply ? $$q : $q).defer(),
promise = deferred.promise;
count = isDefined(count) ? count : 0;
promise.then(null, null, (!hasParams) ? fn : function() {
fn.apply(null, args);
});
promise.$$intervalId = setInterval(function tick() {
deferred.notify(iteration++);
if (count > 0 && iteration >= count) {
deferred.resolve(iteration);
clearInterval(promise.$$intervalId);
delete intervals[promise.$$intervalId];
}
if (!skipApply) $rootScope.$apply();
}, delay);
intervals[promise.$$intervalId] = deferred;
return promise;
}
/**
* @ngdoc method
* @name $interval#cancel
*
* @description
* Cancels a task associated with the `promise`.
*
* @param {promise} promise returned by the `$interval` function.
* @returns {boolean} Returns `true` if the task was successfully canceled.
*/
interval.cancel = function(promise) {
if (promise && promise.$$intervalId in intervals) {
intervals[promise.$$intervalId].reject('canceled');
$window.clearInterval(promise.$$intervalId);
delete intervals[promise.$$intervalId];
return true;
}
return false;
};
return interval;
}];
}
/**
* @ngdoc service
* @name $locale
*
* @description
* $locale service provides localization rules for various Angular components. As of right now the
* only public api is:
*
* * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
*/
function $LocaleProvider() {
this.$get = function() {
return {
id: 'en-us',
NUMBER_FORMATS: {
DECIMAL_SEP: '.',
GROUP_SEP: ',',
PATTERNS: [
{ // Decimal Pattern
minInt: 1,
minFrac: 0,
maxFrac: 3,
posPre: '',
posSuf: '',
negPre: '-',
negSuf: '',
gSize: 3,
lgSize: 3
},{ //Currency Pattern
minInt: 1,
minFrac: 2,
maxFrac: 2,
posPre: '\u00A4',
posSuf: '',
negPre: '(\u00A4',
negSuf: ')',
gSize: 3,
lgSize: 3
}
],
CURRENCY_SYM: '$'
},
DATETIME_FORMATS: {
MONTH:
'January,February,March,April,May,June,July,August,September,October,November,December'
.split(','),
SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
AMPMS: ['AM','PM'],
medium: 'MMM d, y h:mm:ss a',
'short': 'M/d/yy h:mm a',
fullDate: 'EEEE, MMMM d, y',
longDate: 'MMMM d, y',
mediumDate: 'MMM d, y',
shortDate: 'M/d/yy',
mediumTime: 'h:mm:ss a',
shortTime: 'h:mm a',
ERANAMES: [
"Before Christ",
"Anno Domini"
],
ERAS: [
"BC",
"AD"
]
},
pluralCat: function(num) {
if (num === 1) {
return 'one';
}
return 'other';
}
};
};
}
var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
var $locationMinErr = minErr('$location');
/**
* Encode path using encodeUriSegment, ignoring forward slashes
*
* @param {string} path Path to encode
* @returns {string}
*/
function encodePath(path) {
var segments = path.split('/'),
i = segments.length;
while (i--) {
segments[i] = encodeUriSegment(segments[i]);
}
return segments.join('/');
}
function parseAbsoluteUrl(absoluteUrl, locationObj) {
var parsedUrl = urlResolve(absoluteUrl);
locationObj.$$protocol = parsedUrl.protocol;
locationObj.$$host = parsedUrl.hostname;
locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
}
function parseAppUrl(relativeUrl, locationObj) {
var prefixed = (relativeUrl.charAt(0) !== '/');
if (prefixed) {
relativeUrl = '/' + relativeUrl;
}
var match = urlResolve(relativeUrl);
locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
match.pathname.substring(1) : match.pathname);
locationObj.$$search = parseKeyValue(match.search);
locationObj.$$hash = decodeURIComponent(match.hash);
// make sure path starts with '/';
if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
locationObj.$$path = '/' + locationObj.$$path;
}
}
/**
*
* @param {string} begin
* @param {string} whole
* @returns {string} returns text from whole after begin or undefined if it does not begin with
* expected string.
*/
function beginsWith(begin, whole) {
if (whole.indexOf(begin) === 0) {
return whole.substr(begin.length);
}
}
function stripHash(url) {
var index = url.indexOf('#');
return index == -1 ? url : url.substr(0, index);
}
function trimEmptyHash(url) {
return url.replace(/(#.+)|#$/, '$1');
}
function stripFile(url) {
return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
}
/* return the server only (scheme://host:port) */
function serverBase(url) {
return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
}
/**
* LocationHtml5Url represents an url
* This object is exposed as $location service when HTML5 mode is enabled and supported
*
* @constructor
* @param {string} appBase application base URL
* @param {string} basePrefix url path prefix
*/
function LocationHtml5Url(appBase, basePrefix) {
this.$$html5 = true;
basePrefix = basePrefix || '';
var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this);
/**
* Parse given html5 (regular) url string into properties
* @param {string} url HTML5 url
* @private
*/
this.$$parse = function(url) {
var pathUrl = beginsWith(appBaseNoFile, url);
if (!isString(pathUrl)) {
throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
appBaseNoFile);
}
parseAppUrl(pathUrl, this);
if (!this.$$path) {
this.$$path = '/';
}
this.$$compose();
};
/**
* Compose url and update `absUrl` property
* @private
*/
this.$$compose = function() {
var search = toKeyValue(this.$$search),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
};
this.$$parseLinkUrl = function(url, relHref) {
if (relHref && relHref[0] === '#') {
// special case for links to hash fragments:
// keep the old url and only replace the hash fragment
this.hash(relHref.slice(1));
return true;
}
var appUrl, prevAppUrl;
var rewrittenUrl;
if ((appUrl = beginsWith(appBase, url)) !== undefined) {
prevAppUrl = appUrl;
if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {
rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
} else {
rewrittenUrl = appBase + prevAppUrl;
}
} else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {
rewrittenUrl = appBaseNoFile + appUrl;
} else if (appBaseNoFile == url + '/') {
rewrittenUrl = appBaseNoFile;
}
if (rewrittenUrl) {
this.$$parse(rewrittenUrl);
}
return !!rewrittenUrl;
};
}
/**
* LocationHashbangUrl represents url
* This object is exposed as $location service when developer doesn't opt into html5 mode.
* It also serves as the base class for html5 mode fallback on legacy browsers.
*
* @constructor
* @param {string} appBase application base URL
* @param {string} hashPrefix hashbang prefix
*/
function LocationHashbangUrl(appBase, hashPrefix) {
var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this);
/**
* Parse given hashbang url into properties
* @param {string} url Hashbang url
* @private
*/
this.$$parse = function(url) {
var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
var withoutHashUrl;
if (withoutBaseUrl.charAt(0) === '#') {
// The rest of the url starts with a hash so we have
// got either a hashbang path or a plain hash fragment
withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
if (isUndefined(withoutHashUrl)) {
// There was no hashbang prefix so we just have a hash fragment
withoutHashUrl = withoutBaseUrl;
}
} else {
// There was no hashbang path nor hash fragment:
// If we are in HTML5 mode we use what is left as the path;
// Otherwise we ignore what is left
withoutHashUrl = this.$$html5 ? withoutBaseUrl : '';
}
parseAppUrl(withoutHashUrl, this);
this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
this.$$compose();
/*
* In Windows, on an anchor node on documents loaded from
* the filesystem, the browser will return a pathname
* prefixed with the drive name ('/C:/path') when a
* pathname without a drive is set:
* * a.setAttribute('href', '/foo')
* * a.pathname === '/C:/foo' //true
*
* Inside of Angular, we're always using pathnames that
* do not include drive names for routing.
*/
function removeWindowsDriveName(path, url, base) {
/*
Matches paths for file protocol on windows,
such as /C:/foo/bar, and captures only /foo/bar.
*/
var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
var firstPathSegmentMatch;
//Get the relative path from the input URL.
if (url.indexOf(base) === 0) {
url = url.replace(base, '');
}
// The input URL intentionally contains a first path segment that ends with a colon.
if (windowsFilePathExp.exec(url)) {
return path;
}
firstPathSegmentMatch = windowsFilePathExp.exec(path);
return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
}
};
/**
* Compose hashbang url and update `absUrl` property
* @private
*/
this.$$compose = function() {
var search = toKeyValue(this.$$search),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
};
this.$$parseLinkUrl = function(url, relHref) {
if (stripHash(appBase) == stripHash(url)) {
this.$$parse(url);
return true;
}
return false;
};
}
/**
* LocationHashbangUrl represents url
* This object is exposed as $location service when html5 history api is enabled but the browser
* does not support it.
*
* @constructor
* @param {string} appBase application base URL
* @param {string} hashPrefix hashbang prefix
*/
function LocationHashbangInHtml5Url(appBase, hashPrefix) {
this.$$html5 = true;
LocationHashbangUrl.apply(this, arguments);
var appBaseNoFile = stripFile(appBase);
this.$$parseLinkUrl = function(url, relHref) {
if (relHref && relHref[0] === '#') {
// special case for links to hash fragments:
// keep the old url and only replace the hash fragment
this.hash(relHref.slice(1));
return true;
}
var rewrittenUrl;
var appUrl;
if (appBase == stripHash(url)) {
rewrittenUrl = url;
} else if ((appUrl = beginsWith(appBaseNoFile, url))) {
rewrittenUrl = appBase + hashPrefix + appUrl;
} else if (appBaseNoFile === url + '/') {
rewrittenUrl = appBaseNoFile;
}
if (rewrittenUrl) {
this.$$parse(rewrittenUrl);
}
return !!rewrittenUrl;
};
this.$$compose = function() {
var search = toKeyValue(this.$$search),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
// include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
this.$$absUrl = appBase + hashPrefix + this.$$url;
};
}
var locationPrototype = {
/**
* Are we in html5 mode?
* @private
*/
$$html5: false,
/**
* Has any change been replacing?
* @private
*/
$$replace: false,
/**
* @ngdoc method
* @name $location#absUrl
*
* @description
* This method is getter only.
*
* Return full url representation with all segments encoded according to rules specified in
* [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var absUrl = $location.absUrl();
* // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
* ```
*
* @return {string} full url
*/
absUrl: locationGetter('$$absUrl'),
/**
* @ngdoc method
* @name $location#url
*
* @description
* This method is getter / setter.
*
* Return url (e.g. `/path?a=b#hash`) when called without any parameter.
*
* Change path, search and hash, when called with parameter and return `$location`.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var url = $location.url();
* // => "/some/path?foo=bar&baz=xoxo"
* ```
*
* @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
* @return {string} url
*/
url: function(url) {
if (isUndefined(url)) {
return this.$$url;
}
var match = PATH_MATCH.exec(url);
if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
if (match[2] || match[1] || url === '') this.search(match[3] || '');
this.hash(match[5] || '');
return this;
},
/**
* @ngdoc method
* @name $location#protocol
*
* @description
* This method is getter only.
*
* Return protocol of current url.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var protocol = $location.protocol();
* // => "http"
* ```
*
* @return {string} protocol of current url
*/
protocol: locationGetter('$$protocol'),
/**
* @ngdoc method
* @name $location#host
*
* @description
* This method is getter only.
*
* Return host of current url.
*
* Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var host = $location.host();
* // => "example.com"
*
* // given url http://user:[email protected]:8080/#/some/path?foo=bar&baz=xoxo
* host = $location.host();
* // => "example.com"
* host = location.host;
* // => "example.com:8080"
* ```
*
* @return {string} host of current url.
*/
host: locationGetter('$$host'),
/**
* @ngdoc method
* @name $location#port
*
* @description
* This method is getter only.
*
* Return port of current url.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var port = $location.port();
* // => 80
* ```
*
* @return {Number} port
*/
port: locationGetter('$$port'),
/**
* @ngdoc method
* @name $location#path
*
* @description
* This method is getter / setter.
*
* Return path of current url when called without any parameter.
*
* Change path when called with parameter and return `$location`.
*
* Note: Path should always begin with forward slash (/), this method will add the forward slash
* if it is missing.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var path = $location.path();
* // => "/some/path"
* ```
*
* @param {(string|number)=} path New path
* @return {string} path
*/
path: locationGetterSetter('$$path', function(path) {
path = path !== null ? path.toString() : '';
return path.charAt(0) == '/' ? path : '/' + path;
}),
/**
* @ngdoc method
* @name $location#search
*
* @description
* This method is getter / setter.
*
* Return search part (as object) of current url when called without any parameter.
*
* Change search part when called with parameter and return `$location`.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var searchObject = $location.search();
* // => {foo: 'bar', baz: 'xoxo'}
*
* // set foo to 'yipee'
* $location.search('foo', 'yipee');
* // $location.search() => {foo: 'yipee', baz: 'xoxo'}
* ```
*
* @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
* hash object.
*
* When called with a single argument the method acts as a setter, setting the `search` component
* of `$location` to the specified value.
*
* If the argument is a hash object containing an array of values, these values will be encoded
* as duplicate search parameters in the url.
*
* @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
* will override only a single search property.
*
* If `paramValue` is an array, it will override the property of the `search` component of
* `$location` specified via the first argument.
*
* If `paramValue` is `null`, the property specified via the first argument will be deleted.
*
* If `paramValue` is `true`, the property specified via the first argument will be added with no
* value nor trailing equal sign.
*
* @return {Object} If called with no arguments returns the parsed `search` object. If called with
* one or more arguments returns `$location` object itself.
*/
search: function(search, paramValue) {
switch (arguments.length) {
case 0:
return this.$$search;
case 1:
if (isString(search) || isNumber(search)) {
search = search.toString();
this.$$search = parseKeyValue(search);
} else if (isObject(search)) {
search = copy(search, {});
// remove object undefined or null properties
forEach(search, function(value, key) {
if (value == null) delete search[key];
});
this.$$search = search;
} else {
throw $locationMinErr('isrcharg',
'The first argument of the `$location#search()` call must be a string or an object.');
}
break;
default:
if (isUndefined(paramValue) || paramValue === null) {
delete this.$$search[search];
} else {
this.$$search[search] = paramValue;
}
}
this.$$compose();
return this;
},
/**
* @ngdoc method
* @name $location#hash
*
* @description
* This method is getter / setter.
*
* Return hash fragment when called without any parameter.
*
* Change hash fragment when called with parameter and return `$location`.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
* var hash = $location.hash();
* // => "hashValue"
* ```
*
* @param {(string|number)=} hash New hash fragment
* @return {string} hash
*/
hash: locationGetterSetter('$$hash', function(hash) {
return hash !== null ? hash.toString() : '';
}),
/**
* @ngdoc method
* @name $location#replace
*
* @description
* If called, all changes to $location during current `$digest` will be replacing current history
* record, instead of adding new one.
*/
replace: function() {
this.$$replace = true;
return this;
}
};
forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
Location.prototype = Object.create(locationPrototype);
/**
* @ngdoc method
* @name $location#state
*
* @description
* This method is getter / setter.
*
* Return the history state object when called without any parameter.
*
* Change the history state object when called with one parameter and return `$location`.
* The state object is later passed to `pushState` or `replaceState`.
*
* NOTE: This method is supported only in HTML5 mode and only in browsers supporting
* the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
* older browsers (like IE9 or Android < 4.0), don't use this method.
*
* @param {object=} state State object for pushState or replaceState
* @return {object} state
*/
Location.prototype.state = function(state) {
if (!arguments.length) {
return this.$$state;
}
if (Location !== LocationHtml5Url || !this.$$html5) {
throw $locationMinErr('nostate', 'History API state support is available only ' +
'in HTML5 mode and only in browsers supporting HTML5 History API');
}
// The user might modify `stateObject` after invoking `$location.state(stateObject)`
// but we're changing the $$state reference to $browser.state() during the $digest
// so the modification window is narrow.
this.$$state = isUndefined(state) ? null : state;
return this;
};
});
function locationGetter(property) {
return function() {
return this[property];
};
}
function locationGetterSetter(property, preprocess) {
return function(value) {
if (isUndefined(value)) {
return this[property];
}
this[property] = preprocess(value);
this.$$compose();
return this;
};
}
/**
* @ngdoc service
* @name $location
*
* @requires $rootElement
*
* @description
* The $location service parses the URL in the browser address bar (based on the
* [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
* available to your application. Changes to the URL in the address bar are reflected into
* $location service and changes to $location are reflected into the browser address bar.
*
* **The $location service:**
*
* - Exposes the current URL in the browser address bar, so you can
* - Watch and observe the URL.
* - Change the URL.
* - Synchronizes the URL with the browser when the user
* - Changes the address bar.
* - Clicks the back or forward button (or clicks a History link).
* - Clicks on a link.
* - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
*
* For more information see {@link guide/$location Developer Guide: Using $location}
*/
/**
* @ngdoc provider
* @name $locationProvider
* @description
* Use the `$locationProvider` to configure how the application deep linking paths are stored.
*/
function $LocationProvider() {
var hashPrefix = '',
html5Mode = {
enabled: false,
requireBase: true,
rewriteLinks: true
};
/**
* @ngdoc method
* @name $locationProvider#hashPrefix
* @description
* @param {string=} prefix Prefix for hash part (containing path and search)
* @returns {*} current value if used as getter or itself (chaining) if used as setter
*/
this.hashPrefix = function(prefix) {
if (isDefined(prefix)) {
hashPrefix = prefix;
return this;
} else {
return hashPrefix;
}
};
/**
* @ngdoc method
* @name $locationProvider#html5Mode
* @description
* @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
* If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
* properties:
* - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
* change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
* support `pushState`.
* - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
* whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
* true, and a base tag is not present, an error will be thrown when `$location` is injected.
* See the {@link guide/$location $location guide for more information}
* - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
* enables/disables url rewriting for relative links.
*
* @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
*/
this.html5Mode = function(mode) {
if (isBoolean(mode)) {
html5Mode.enabled = mode;
return this;
} else if (isObject(mode)) {
if (isBoolean(mode.enabled)) {
html5Mode.enabled = mode.enabled;
}
if (isBoolean(mode.requireBase)) {
html5Mode.requireBase = mode.requireBase;
}
if (isBoolean(mode.rewriteLinks)) {
html5Mode.rewriteLinks = mode.rewriteLinks;
}
return this;
} else {
return html5Mode;
}
};
/**
* @ngdoc event
* @name $location#$locationChangeStart
* @eventType broadcast on root scope
* @description
* Broadcasted before a URL will change.
*
* This change can be prevented by calling
* `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
* details about event object. Upon successful change
* {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
*
* The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
* the browser supports the HTML5 History API.
*
* @param {Object} angularEvent Synthetic event object.
* @param {string} newUrl New URL
* @param {string=} oldUrl URL that was before it was changed.
* @param {string=} newState New history state object
* @param {string=} oldState History state object that was before it was changed.
*/
/**
* @ngdoc event
* @name $location#$locationChangeSuccess
* @eventType broadcast on root scope
* @description
* Broadcasted after a URL was changed.
*
* The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
* the browser supports the HTML5 History API.
*
* @param {Object} angularEvent Synthetic event object.
* @param {string} newUrl New URL
* @param {string=} oldUrl URL that was before it was changed.
* @param {string=} newState New history state object
* @param {string=} oldState History state object that was before it was changed.
*/
this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
function($rootScope, $browser, $sniffer, $rootElement, $window) {
var $location,
LocationMode,
baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
initialUrl = $browser.url(),
appBase;
if (html5Mode.enabled) {
if (!baseHref && html5Mode.requireBase) {
throw $locationMinErr('nobase',
"$location in HTML5 mode requires a <base> tag to be present!");
}
appBase = serverBase(initialUrl) + (baseHref || '/');
LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
} else {
appBase = stripHash(initialUrl);
LocationMode = LocationHashbangUrl;
}
$location = new LocationMode(appBase, '#' + hashPrefix);
$location.$$parseLinkUrl(initialUrl, initialUrl);
$location.$$state = $browser.state();
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
function setBrowserUrlWithFallback(url, replace, state) {
var oldUrl = $location.url();
var oldState = $location.$$state;
try {
$browser.url(url, replace, state);
// Make sure $location.state() returns referentially identical (not just deeply equal)
// state object; this makes possible quick checking if the state changed in the digest
// loop. Checking deep equality would be too expensive.
$location.$$state = $browser.state();
} catch (e) {
// Restore old values if pushState fails
$location.url(oldUrl);
$location.$$state = oldState;
throw e;
}
}
$rootElement.on('click', function(event) {
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
// currently we open nice url link and redirect then
if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
var elm = jqLite(event.target);
// traverse the DOM up to find first A tag
while (nodeName_(elm[0]) !== 'a') {
// ignore rewriting if no A tag (reached root element, or no parent - removed from document)
if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
}
var absHref = elm.prop('href');
// get the actual href attribute - see
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
var relHref = elm.attr('href') || elm.attr('xlink:href');
if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
// an animation.
absHref = urlResolve(absHref.animVal).href;
}
// Ignore when url is started with javascript: or mailto:
if (IGNORE_URI_REGEXP.test(absHref)) return;
if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
if ($location.$$parseLinkUrl(absHref, relHref)) {
// We do a preventDefault for all urls that are part of the angular application,
// in html5mode and also without, so that we are able to abort navigation without
// getting double entries in the location history.
event.preventDefault();
// update location manually
if ($location.absUrl() != $browser.url()) {
$rootScope.$apply();
// hack to work around FF6 bug 684208 when scenario runner clicks on links
$window.angular['ff-684208-preventDefault'] = true;
}
}
}
});
// rewrite hashbang url <> html5 url
if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
$browser.url($location.absUrl(), true);
}
var initializing = true;
// update $location when $browser url changes
$browser.onUrlChange(function(newUrl, newState) {
$rootScope.$evalAsync(function() {
var oldUrl = $location.absUrl();
var oldState = $location.$$state;
var defaultPrevented;
$location.$$parse(newUrl);
$location.$$state = newState;
defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
newState, oldState).defaultPrevented;
// if the location was changed by a `$locationChangeStart` handler then stop
// processing this location change
if ($location.absUrl() !== newUrl) return;
if (defaultPrevented) {
$location.$$parse(oldUrl);
$location.$$state = oldState;
setBrowserUrlWithFallback(oldUrl, false, oldState);
} else {
initializing = false;
afterLocationChange(oldUrl, oldState);
}
});
if (!$rootScope.$$phase) $rootScope.$digest();
});
// update browser
$rootScope.$watch(function $locationWatch() {
var oldUrl = trimEmptyHash($browser.url());
var newUrl = trimEmptyHash($location.absUrl());
var oldState = $browser.state();
var currentReplace = $location.$$replace;
var urlOrStateChanged = oldUrl !== newUrl ||
($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
if (initializing || urlOrStateChanged) {
initializing = false;
$rootScope.$evalAsync(function() {
var newUrl = $location.absUrl();
var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
$location.$$state, oldState).defaultPrevented;
// if the location was changed by a `$locationChangeStart` handler then stop
// processing this location change
if ($location.absUrl() !== newUrl) return;
if (defaultPrevented) {
$location.$$parse(oldUrl);
$location.$$state = oldState;
} else {
if (urlOrStateChanged) {
setBrowserUrlWithFallback(newUrl, currentReplace,
oldState === $location.$$state ? null : $location.$$state);
}
afterLocationChange(oldUrl, oldState);
}
});
}
$location.$$replace = false;
// we don't need to return anything because $evalAsync will make the digest loop dirty when
// there is a change
});
return $location;
function afterLocationChange(oldUrl, oldState) {
$rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
$location.$$state, oldState);
}
}];
}
/**
* @ngdoc service
* @name $log
* @requires $window
*
* @description
* Simple service for logging. Default implementation safely writes the message
* into the browser's console (if present).
*
* The main purpose of this service is to simplify debugging and troubleshooting.
*
* The default is to log `debug` messages. You can use
* {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
*
* @example
<example module="logExample">
<file name="script.js">
angular.module('logExample', [])
.controller('LogController', ['$scope', '$log', function($scope, $log) {
$scope.$log = $log;
$scope.message = 'Hello World!';
}]);
</file>
<file name="index.html">
<div ng-controller="LogController">
<p>Reload this page with open console, enter text and hit the log button...</p>
<label>Message:
<input type="text" ng-model="message" /></label>
<button ng-click="$log.log(message)">log</button>
<button ng-click="$log.warn(message)">warn</button>
<button ng-click="$log.info(message)">info</button>
<button ng-click="$log.error(message)">error</button>
<button ng-click="$log.debug(message)">debug</button>
</div>
</file>
</example>
*/
/**
* @ngdoc provider
* @name $logProvider
* @description
* Use the `$logProvider` to configure how the application logs messages
*/
function $LogProvider() {
var debug = true,
self = this;
/**
* @ngdoc method
* @name $logProvider#debugEnabled
* @description
* @param {boolean=} flag enable or disable debug level messages
* @returns {*} current value if used as getter or itself (chaining) if used as setter
*/
this.debugEnabled = function(flag) {
if (isDefined(flag)) {
debug = flag;
return this;
} else {
return debug;
}
};
this.$get = ['$window', function($window) {
return {
/**
* @ngdoc method
* @name $log#log
*
* @description
* Write a log message
*/
log: consoleLog('log'),
/**
* @ngdoc method
* @name $log#info
*
* @description
* Write an information message
*/
info: consoleLog('info'),
/**
* @ngdoc method
* @name $log#warn
*
* @description
* Write a warning message
*/
warn: consoleLog('warn'),
/**
* @ngdoc method
* @name $log#error
*
* @description
* Write an error message
*/
error: consoleLog('error'),
/**
* @ngdoc method
* @name $log#debug
*
* @description
* Write a debug message
*/
debug: (function() {
var fn = consoleLog('debug');
return function() {
if (debug) {
fn.apply(self, arguments);
}
};
}())
};
function formatError(arg) {
if (arg instanceof Error) {
if (arg.stack) {
arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
? 'Error: ' + arg.message + '\n' + arg.stack
: arg.stack;
} else if (arg.sourceURL) {
arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
}
}
return arg;
}
function consoleLog(type) {
var console = $window.console || {},
logFn = console[type] || console.log || noop,
hasApply = false;
// Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
// The reason behind this is that console.log has type "object" in IE8...
try {
hasApply = !!logFn.apply;
} catch (e) {}
if (hasApply) {
return function() {
var args = [];
forEach(arguments, function(arg) {
args.push(formatError(arg));
});
return logFn.apply(console, args);
};
}
// we are IE which either doesn't have window.console => this is noop and we do nothing,
// or we are IE where console.log doesn't have apply so we log at least first 2 args
return function(arg1, arg2) {
logFn(arg1, arg2 == null ? '' : arg2);
};
}
}];
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Any commits to this file should be reviewed with security in mind. *
* Changes to this file can potentially create security vulnerabilities. *
* An approval from 2 Core members with history of modifying *
* this file is required. *
* *
* Does the change somehow allow for arbitrary javascript to be executed? *
* Or allows for someone to change the prototype of built-in objects? *
* Or gives undesired access to variables likes document or window? *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
var $parseMinErr = minErr('$parse');
// Sandboxing Angular Expressions
// ------------------------------
// Angular expressions are generally considered safe because these expressions only have direct
// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
// obtaining a reference to native JS functions such as the Function constructor.
//
// As an example, consider the following Angular expression:
//
// {}.toString.constructor('alert("evil JS code")')
//
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
// against the expression language, but not to prevent exploits that were enabled by exposing
// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
// practice and therefore we are not even trying to protect against interaction with an object
// explicitly exposed in this way.
//
// In general, it is not possible to access a Window object from an angular expression unless a
// window or some DOM object that has a reference to window is published onto a Scope.
// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
// native objects.
//
// See https://docs.angularjs.org/guide/security
function ensureSafeMemberName(name, fullExpression) {
if (name === "__defineGetter__" || name === "__defineSetter__"
|| name === "__lookupGetter__" || name === "__lookupSetter__"
|| name === "__proto__") {
throw $parseMinErr('isecfld',
'Attempting to access a disallowed field in Angular expressions! '
+ 'Expression: {0}', fullExpression);
}
return name;
}
function ensureSafeObject(obj, fullExpression) {
// nifty check if obj is Function that is fast and works across iframes and other contexts
if (obj) {
if (obj.constructor === obj) {
throw $parseMinErr('isecfn',
'Referencing Function in Angular expressions is disallowed! Expression: {0}',
fullExpression);
} else if (// isWindow(obj)
obj.window === obj) {
throw $parseMinErr('isecwindow',
'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
fullExpression);
} else if (// isElement(obj)
obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
throw $parseMinErr('isecdom',
'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
fullExpression);
} else if (// block Object so that we can't get hold of dangerous Object.* methods
obj === Object) {
throw $parseMinErr('isecobj',
'Referencing Object in Angular expressions is disallowed! Expression: {0}',
fullExpression);
}
}
return obj;
}
var CALL = Function.prototype.call;
var APPLY = Function.prototype.apply;
var BIND = Function.prototype.bind;
function ensureSafeFunction(obj, fullExpression) {
if (obj) {
if (obj.constructor === obj) {
throw $parseMinErr('isecfn',
'Referencing Function in Angular expressions is disallowed! Expression: {0}',
fullExpression);
} else if (obj === CALL || obj === APPLY || obj === BIND) {
throw $parseMinErr('isecff',
'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
fullExpression);
}
}
}
var OPERATORS = createMap();
forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
/////////////////////////////////////////
/**
* @constructor
*/
var Lexer = function(options) {
this.options = options;
};
Lexer.prototype = {
constructor: Lexer,
lex: function(text) {
this.text = text;
this.index = 0;
this.tokens = [];
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
if (ch === '"' || ch === "'") {
this.readString(ch);
} else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
this.readNumber();
} else if (this.isIdent(ch)) {
this.readIdent();
} else if (this.is(ch, '(){}[].,;:?')) {
this.tokens.push({index: this.index, text: ch});
this.index++;
} else if (this.isWhitespace(ch)) {
this.index++;
} else {
var ch2 = ch + this.peek();
var ch3 = ch2 + this.peek(2);
var op1 = OPERATORS[ch];
var op2 = OPERATORS[ch2];
var op3 = OPERATORS[ch3];
if (op1 || op2 || op3) {
var token = op3 ? ch3 : (op2 ? ch2 : ch);
this.tokens.push({index: this.index, text: token, operator: true});
this.index += token.length;
} else {
this.throwError('Unexpected next character ', this.index, this.index + 1);
}
}
}
return this.tokens;
},
is: function(ch, chars) {
return chars.indexOf(ch) !== -1;
},
peek: function(i) {
var num = i || 1;
return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
},
isNumber: function(ch) {
return ('0' <= ch && ch <= '9') && typeof ch === "string";
},
isWhitespace: function(ch) {
// IE treats non-breaking space as \u00A0
return (ch === ' ' || ch === '\r' || ch === '\t' ||
ch === '\n' || ch === '\v' || ch === '\u00A0');
},
isIdent: function(ch) {
return ('a' <= ch && ch <= 'z' ||
'A' <= ch && ch <= 'Z' ||
'_' === ch || ch === '$');
},
isExpOperator: function(ch) {
return (ch === '-' || ch === '+' || this.isNumber(ch));
},
throwError: function(error, start, end) {
end = end || this.index;
var colStr = (isDefined(start)
? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
: ' ' + end);
throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
error, colStr, this.text);
},
readNumber: function() {
var number = '';
var start = this.index;
while (this.index < this.text.length) {
var ch = lowercase(this.text.charAt(this.index));
if (ch == '.' || this.isNumber(ch)) {
number += ch;
} else {
var peekCh = this.peek();
if (ch == 'e' && this.isExpOperator(peekCh)) {
number += ch;
} else if (this.isExpOperator(ch) &&
peekCh && this.isNumber(peekCh) &&
number.charAt(number.length - 1) == 'e') {
number += ch;
} else if (this.isExpOperator(ch) &&
(!peekCh || !this.isNumber(peekCh)) &&
number.charAt(number.length - 1) == 'e') {
this.throwError('Invalid exponent');
} else {
break;
}
}
this.index++;
}
this.tokens.push({
index: start,
text: number,
constant: true,
value: Number(number)
});
},
readIdent: function() {
var start = this.index;
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
if (!(this.isIdent(ch) || this.isNumber(ch))) {
break;
}
this.index++;
}
this.tokens.push({
index: start,
text: this.text.slice(start, this.index),
identifier: true
});
},
readString: function(quote) {
var start = this.index;
this.index++;
var string = '';
var rawString = quote;
var escape = false;
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
rawString += ch;
if (escape) {
if (ch === 'u') {
var hex = this.text.substring(this.index + 1, this.index + 5);
if (!hex.match(/[\da-f]{4}/i)) {
this.throwError('Invalid unicode escape [\\u' + hex + ']');
}
this.index += 4;
string += String.fromCharCode(parseInt(hex, 16));
} else {
var rep = ESCAPE[ch];
string = string + (rep || ch);
}
escape = false;
} else if (ch === '\\') {
escape = true;
} else if (ch === quote) {
this.index++;
this.tokens.push({
index: start,
text: rawString,
constant: true,
value: string
});
return;
} else {
string += ch;
}
this.index++;
}
this.throwError('Unterminated quote', start);
}
};
var AST = function(lexer, options) {
this.lexer = lexer;
this.options = options;
};
AST.Program = 'Program';
AST.ExpressionStatement = 'ExpressionStatement';
AST.AssignmentExpression = 'AssignmentExpression';
AST.ConditionalExpression = 'ConditionalExpression';
AST.LogicalExpression = 'LogicalExpression';
AST.BinaryExpression = 'BinaryExpression';
AST.UnaryExpression = 'UnaryExpression';
AST.CallExpression = 'CallExpression';
AST.MemberExpression = 'MemberExpression';
AST.Identifier = 'Identifier';
AST.Literal = 'Literal';
AST.ArrayExpression = 'ArrayExpression';
AST.Property = 'Property';
AST.ObjectExpression = 'ObjectExpression';
AST.ThisExpression = 'ThisExpression';
// Internal use only
AST.NGValueParameter = 'NGValueParameter';
AST.prototype = {
ast: function(text) {
this.text = text;
this.tokens = this.lexer.lex(text);
var value = this.program();
if (this.tokens.length !== 0) {
this.throwError('is an unexpected token', this.tokens[0]);
}
return value;
},
program: function() {
var body = [];
while (true) {
if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
body.push(this.expressionStatement());
if (!this.expect(';')) {
return { type: AST.Program, body: body};
}
}
},
expressionStatement: function() {
return { type: AST.ExpressionStatement, expression: this.filterChain() };
},
filterChain: function() {
var left = this.expression();
var token;
while ((token = this.expect('|'))) {
left = this.filter(left);
}
return left;
},
expression: function() {
return this.assignment();
},
assignment: function() {
var result = this.ternary();
if (this.expect('=')) {
result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
}
return result;
},
ternary: function() {
var test = this.logicalOR();
var alternate;
var consequent;
if (this.expect('?')) {
alternate = this.expression();
if (this.consume(':')) {
consequent = this.expression();
return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
}
}
return test;
},
logicalOR: function() {
var left = this.logicalAND();
while (this.expect('||')) {
left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
}
return left;
},
logicalAND: function() {
var left = this.equality();
while (this.expect('&&')) {
left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
}
return left;
},
equality: function() {
var left = this.relational();
var token;
while ((token = this.expect('==','!=','===','!=='))) {
left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
}
return left;
},
relational: function() {
var left = this.additive();
var token;
while ((token = this.expect('<', '>', '<=', '>='))) {
left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
}
return left;
},
additive: function() {
var left = this.multiplicative();
var token;
while ((token = this.expect('+','-'))) {
left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
}
return left;
},
multiplicative: function() {
var left = this.unary();
var token;
while ((token = this.expect('*','/','%'))) {
left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
}
return left;
},
unary: function() {
var token;
if ((token = this.expect('+', '-', '!'))) {
return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
} else {
return this.primary();
}
},
primary: function() {
var primary;
if (this.expect('(')) {
primary = this.filterChain();
this.consume(')');
} else if (this.expect('[')) {
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.peek().identifier) {
primary = this.identifier();
} else if (this.peek().constant) {
primary = this.constant();
} else {
this.throwError('not a primary expression', this.peek());
}
var next;
while ((next = this.expect('(', '[', '.'))) {
if (next.text === '(') {
primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
this.consume(')');
} else if (next.text === '[') {
primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
this.consume(']');
} else if (next.text === '.') {
primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
} else {
this.throwError('IMPOSSIBLE');
}
}
return primary;
},
filter: function(baseExpression) {
var args = [baseExpression];
var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
while (this.expect(':')) {
args.push(this.expression());
}
return result;
},
parseArguments: function() {
var args = [];
if (this.peekToken().text !== ')') {
do {
args.push(this.expression());
} while (this.expect(','));
}
return args;
},
identifier: function() {
var token = this.consume();
if (!token.identifier) {
this.throwError('is not a valid identifier', token);
}
return { type: AST.Identifier, name: token.text };
},
constant: function() {
// TODO check that it is a constant
return { type: AST.Literal, value: this.consume().value };
},
arrayDeclaration: function() {
var elements = [];
if (this.peekToken().text !== ']') {
do {
if (this.peek(']')) {
// Support trailing commas per ES5.1.
break;
}
elements.push(this.expression());
} while (this.expect(','));
}
this.consume(']');
return { type: AST.ArrayExpression, elements: elements };
},
object: function() {
var properties = [], property;
if (this.peekToken().text !== '}') {
do {
if (this.peek('}')) {
// Support trailing commas per ES5.1.
break;
}
property = {type: AST.Property, kind: 'init'};
if (this.peek().constant) {
property.key = this.constant();
} else if (this.peek().identifier) {
property.key = this.identifier();
} else {
this.throwError("invalid key", this.peek());
}
this.consume(':');
property.value = this.expression();
properties.push(property);
} while (this.expect(','));
}
this.consume('}');
return {type: AST.ObjectExpression, properties: properties };
},
throwError: function(msg, token) {
throw $parseMinErr('syntax',
'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
},
consume: function(e1) {
if (this.tokens.length === 0) {
throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
}
var token = this.expect(e1);
if (!token) {
this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
}
return token;
},
peekToken: function() {
if (this.tokens.length === 0) {
throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
}
return this.tokens[0];
},
peek: function(e1, e2, e3, e4) {
return this.peekAhead(0, e1, e2, e3, e4);
},
peekAhead: function(i, e1, e2, e3, e4) {
if (this.tokens.length > i) {
var token = this.tokens[i];
var t = token.text;
if (t === e1 || t === e2 || t === e3 || t === e4 ||
(!e1 && !e2 && !e3 && !e4)) {
return token;
}
}
return false;
},
expect: function(e1, e2, e3, e4) {
var token = this.peek(e1, e2, e3, e4);
if (token) {
this.tokens.shift();
return token;
}
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 },
'this': {type: AST.ThisExpression }
}
};
function ifDefined(v, d) {
return typeof v !== 'undefined' ? v : d;
}
function plusFn(l, r) {
if (typeof l === 'undefined') return r;
if (typeof r === 'undefined') return l;
return l + r;
}
function isStateless($filter, filterName) {
var fn = $filter(filterName);
return !fn.$stateful;
}
function findConstantAndWatchExpressions(ast, $filter) {
var allConstants;
var argsToWatch;
switch (ast.type) {
case AST.Program:
allConstants = true;
forEach(ast.body, function(expr) {
findConstantAndWatchExpressions(expr.expression, $filter);
allConstants = allConstants && expr.expression.constant;
});
ast.constant = allConstants;
break;
case AST.Literal:
ast.constant = true;
ast.toWatch = [];
break;
case AST.UnaryExpression:
findConstantAndWatchExpressions(ast.argument, $filter);
ast.constant = ast.argument.constant;
ast.toWatch = ast.argument.toWatch;
break;
case AST.BinaryExpression:
findConstantAndWatchExpressions(ast.left, $filter);
findConstantAndWatchExpressions(ast.right, $filter);
ast.constant = ast.left.constant && ast.right.constant;
ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
break;
case AST.LogicalExpression:
findConstantAndWatchExpressions(ast.left, $filter);
findConstantAndWatchExpressions(ast.right, $filter);
ast.constant = ast.left.constant && ast.right.constant;
ast.toWatch = ast.constant ? [] : [ast];
break;
case AST.ConditionalExpression:
findConstantAndWatchExpressions(ast.test, $filter);
findConstantAndWatchExpressions(ast.alternate, $filter);
findConstantAndWatchExpressions(ast.consequent, $filter);
ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
ast.toWatch = ast.constant ? [] : [ast];
break;
case AST.Identifier:
ast.constant = false;
ast.toWatch = [ast];
break;
case AST.MemberExpression:
findConstantAndWatchExpressions(ast.object, $filter);
if (ast.computed) {
findConstantAndWatchExpressions(ast.property, $filter);
}
ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
ast.toWatch = [ast];
break;
case AST.CallExpression:
allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
argsToWatch = [];
forEach(ast.arguments, function(expr) {
findConstantAndWatchExpressions(expr, $filter);
allConstants = allConstants && expr.constant;
if (!expr.constant) {
argsToWatch.push.apply(argsToWatch, expr.toWatch);
}
});
ast.constant = allConstants;
ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
break;
case AST.AssignmentExpression:
findConstantAndWatchExpressions(ast.left, $filter);
findConstantAndWatchExpressions(ast.right, $filter);
ast.constant = ast.left.constant && ast.right.constant;
ast.toWatch = [ast];
break;
case AST.ArrayExpression:
allConstants = true;
argsToWatch = [];
forEach(ast.elements, function(expr) {
findConstantAndWatchExpressions(expr, $filter);
allConstants = allConstants && expr.constant;
if (!expr.constant) {
argsToWatch.push.apply(argsToWatch, expr.toWatch);
}
});
ast.constant = allConstants;
ast.toWatch = argsToWatch;
break;
case AST.ObjectExpression:
allConstants = true;
argsToWatch = [];
forEach(ast.properties, function(property) {
findConstantAndWatchExpressions(property.value, $filter);
allConstants = allConstants && property.value.constant;
if (!property.value.constant) {
argsToWatch.push.apply(argsToWatch, property.value.toWatch);
}
});
ast.constant = allConstants;
ast.toWatch = argsToWatch;
break;
case AST.ThisExpression:
ast.constant = false;
ast.toWatch = [];
break;
}
}
function getInputs(body) {
if (body.length != 1) return;
var lastExpression = body[0].expression;
var candidate = lastExpression.toWatch;
if (candidate.length !== 1) return candidate;
return candidate[0] !== lastExpression ? candidate : undefined;
}
function isAssignable(ast) {
return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
}
function assignableAST(ast) {
if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
}
}
function isLiteral(ast) {
return ast.body.length === 0 ||
ast.body.length === 1 && (
ast.body[0].expression.type === AST.Literal ||
ast.body[0].expression.type === AST.ArrayExpression ||
ast.body[0].expression.type === AST.ObjectExpression);
}
function isConstant(ast) {
return ast.constant;
}
function ASTCompiler(astBuilder, $filter) {
this.astBuilder = astBuilder;
this.$filter = $filter;
}
ASTCompiler.prototype = {
compile: function(expression, expensiveChecks) {
var self = this;
var ast = this.astBuilder.ast(expression);
this.state = {
nextId: 0,
filters: {},
expensiveChecks: expensiveChecks,
fn: {vars: [], body: [], own: {}},
assign: {vars: [], body: [], own: {}},
inputs: []
};
findConstantAndWatchExpressions(ast, self.$filter);
var extra = '';
var assignable;
this.stage = 'assign';
if ((assignable = assignableAST(ast))) {
this.state.computing = 'assign';
var result = this.nextId();
this.recurse(assignable, result);
extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
}
var toWatch = getInputs(ast.body);
self.stage = 'inputs';
forEach(toWatch, function(watch, key) {
var fnKey = 'fn' + key;
self.state[fnKey] = {vars: [], body: [], own: {}};
self.state.computing = fnKey;
var intoId = self.nextId();
self.recurse(watch, intoId);
self.return_(intoId);
self.state.inputs.push(fnKey);
watch.watchId = key;
});
this.state.computing = 'fn';
this.stage = 'main';
this.recurse(ast);
var fnString =
// The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
// This is a workaround for this until we do a better job at only removing the prefix only when we should.
'"' + this.USE + ' ' + this.STRICT + '";\n' +
this.filterPrefix() +
'var fn=' + this.generateFunction('fn', 's,l,a,i') +
extra +
this.watchFns() +
'return fn;';
/* jshint -W054 */
var fn = (new Function('$filter',
'ensureSafeMemberName',
'ensureSafeObject',
'ensureSafeFunction',
'ifDefined',
'plus',
'text',
fnString))(
this.$filter,
ensureSafeMemberName,
ensureSafeObject,
ensureSafeFunction,
ifDefined,
plusFn,
expression);
/* jshint +W054 */
this.state = this.stage = undefined;
fn.literal = isLiteral(ast);
fn.constant = isConstant(ast);
return fn;
},
USE: 'use',
STRICT: 'strict',
watchFns: function() {
var result = [];
var fns = this.state.inputs;
var self = this;
forEach(fns, function(name) {
result.push('var ' + name + '=' + self.generateFunction(name, 's'));
});
if (fns.length) {
result.push('fn.inputs=[' + fns.join(',') + '];');
}
return result.join('');
},
generateFunction: function(name, params) {
return 'function(' + params + '){' +
this.varsPrefix(name) +
this.body(name) +
'};';
},
filterPrefix: function() {
var parts = [];
var self = this;
forEach(this.state.filters, function(id, filter) {
parts.push(id + '=$filter(' + self.escape(filter) + ')');
});
if (parts.length) return 'var ' + parts.join(',') + ';';
return '';
},
varsPrefix: function(section) {
return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
},
body: function(section) {
return this.state[section].body.join('');
},
recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
var left, right, self = this, args, expression;
recursionFn = recursionFn || noop;
if (!skipWatchIdCheck && isDefined(ast.watchId)) {
intoId = intoId || this.nextId();
this.if_('i',
this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
);
return;
}
switch (ast.type) {
case AST.Program:
forEach(ast.body, function(expression, pos) {
self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
if (pos !== ast.body.length - 1) {
self.current().body.push(right, ';');
} else {
self.return_(right);
}
});
break;
case AST.Literal:
expression = this.escape(ast.value);
this.assign(intoId, expression);
recursionFn(expression);
break;
case AST.UnaryExpression:
this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
this.assign(intoId, expression);
recursionFn(expression);
break;
case AST.BinaryExpression:
this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
if (ast.operator === '+') {
expression = this.plus(left, right);
} else if (ast.operator === '-') {
expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
} else {
expression = '(' + left + ')' + ast.operator + '(' + right + ')';
}
this.assign(intoId, expression);
recursionFn(expression);
break;
case AST.LogicalExpression:
intoId = intoId || this.nextId();
self.recurse(ast.left, intoId);
self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
recursionFn(intoId);
break;
case AST.ConditionalExpression:
intoId = intoId || this.nextId();
self.recurse(ast.test, intoId);
self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
recursionFn(intoId);
break;
case AST.Identifier:
intoId = intoId || this.nextId();
if (nameId) {
nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
nameId.computed = false;
nameId.name = ast.name;
}
ensureSafeMemberName(ast.name);
self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
function() {
self.if_(self.stage === 'inputs' || 's', function() {
if (create && create !== 1) {
self.if_(
self.not(self.nonComputedMember('s', ast.name)),
self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
}
self.assign(intoId, self.nonComputedMember('s', ast.name));
});
}, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
);
if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
self.addEnsureSafeObject(intoId);
}
recursionFn(intoId);
break;
case AST.MemberExpression:
left = nameId && (nameId.context = this.nextId()) || this.nextId();
intoId = intoId || this.nextId();
self.recurse(ast.object, left, undefined, function() {
self.if_(self.notNull(left), function() {
if (ast.computed) {
right = self.nextId();
self.recurse(ast.property, right);
self.addEnsureSafeMemberName(right);
if (create && create !== 1) {
self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
}
expression = self.ensureSafeObject(self.computedMember(left, right));
self.assign(intoId, expression);
if (nameId) {
nameId.computed = true;
nameId.name = right;
}
} else {
ensureSafeMemberName(ast.property.name);
if (create && create !== 1) {
self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
}
expression = self.nonComputedMember(left, ast.property.name);
if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
expression = self.ensureSafeObject(expression);
}
self.assign(intoId, expression);
if (nameId) {
nameId.computed = false;
nameId.name = ast.property.name;
}
}
recursionFn(intoId);
});
}, !!create);
break;
case AST.CallExpression:
intoId = intoId || this.nextId();
if (ast.filter) {
right = self.filter(ast.callee.name);
args = [];
forEach(ast.arguments, function(expr) {
var argument = self.nextId();
self.recurse(expr, argument);
args.push(argument);
});
expression = right + '(' + args.join(',') + ')';
self.assign(intoId, expression);
recursionFn(intoId);
} else {
right = self.nextId();
left = {};
args = [];
self.recurse(ast.callee, right, left, function() {
self.if_(self.notNull(right), function() {
self.addEnsureSafeFunction(right);
forEach(ast.arguments, function(expr) {
self.recurse(expr, self.nextId(), undefined, function(argument) {
args.push(self.ensureSafeObject(argument));
});
});
if (left.name) {
if (!self.state.expensiveChecks) {
self.addEnsureSafeObject(left.context);
}
expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
} else {
expression = right + '(' + args.join(',') + ')';
}
expression = self.ensureSafeObject(expression);
self.assign(intoId, expression);
recursionFn(intoId);
});
});
}
break;
case AST.AssignmentExpression:
right = this.nextId();
left = {};
if (!isAssignable(ast.left)) {
throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
}
this.recurse(ast.left, undefined, left, function() {
self.if_(self.notNull(left.context), function() {
self.recurse(ast.right, right);
self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
self.assign(intoId, expression);
recursionFn(intoId || expression);
});
}, 1);
break;
case AST.ArrayExpression:
args = [];
forEach(ast.elements, function(expr) {
self.recurse(expr, self.nextId(), undefined, function(argument) {
args.push(argument);
});
});
expression = '[' + args.join(',') + ']';
this.assign(intoId, expression);
recursionFn(expression);
break;
case AST.ObjectExpression:
args = [];
forEach(ast.properties, function(property) {
self.recurse(property.value, self.nextId(), undefined, function(expr) {
args.push(self.escape(
property.key.type === AST.Identifier ? property.key.name :
('' + property.key.value)) +
':' + expr);
});
});
expression = '{' + args.join(',') + '}';
this.assign(intoId, expression);
recursionFn(expression);
break;
case AST.ThisExpression:
this.assign(intoId, 's');
recursionFn('s');
break;
case AST.NGValueParameter:
this.assign(intoId, 'v');
recursionFn('v');
break;
}
},
getHasOwnProperty: function(element, property) {
var key = element + '.' + property;
var own = this.current().own;
if (!own.hasOwnProperty(key)) {
own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
}
return own[key];
},
assign: function(id, value) {
if (!id) return;
this.current().body.push(id, '=', value, ';');
return id;
},
filter: function(filterName) {
if (!this.state.filters.hasOwnProperty(filterName)) {
this.state.filters[filterName] = this.nextId(true);
}
return this.state.filters[filterName];
},
ifDefined: function(id, defaultValue) {
return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
},
plus: function(left, right) {
return 'plus(' + left + ',' + right + ')';
},
return_: function(id) {
this.current().body.push('return ', id, ';');
},
if_: function(test, alternate, consequent) {
if (test === true) {
alternate();
} else {
var body = this.current().body;
body.push('if(', test, '){');
alternate();
body.push('}');
if (consequent) {
body.push('else{');
consequent();
body.push('}');
}
}
},
not: function(expression) {
return '!(' + expression + ')';
},
notNull: function(expression) {
return expression + '!=null';
},
nonComputedMember: function(left, right) {
return left + '.' + right;
},
computedMember: function(left, right) {
return left + '[' + right + ']';
},
member: function(left, right, computed) {
if (computed) return this.computedMember(left, right);
return this.nonComputedMember(left, right);
},
addEnsureSafeObject: function(item) {
this.current().body.push(this.ensureSafeObject(item), ';');
},
addEnsureSafeMemberName: function(item) {
this.current().body.push(this.ensureSafeMemberName(item), ';');
},
addEnsureSafeFunction: function(item) {
this.current().body.push(this.ensureSafeFunction(item), ';');
},
ensureSafeObject: function(item) {
return 'ensureSafeObject(' + item + ',text)';
},
ensureSafeMemberName: function(item) {
return 'ensureSafeMemberName(' + item + ',text)';
},
ensureSafeFunction: function(item) {
return 'ensureSafeFunction(' + item + ',text)';
},
lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
var self = this;
return function() {
self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
};
},
lazyAssign: function(id, value) {
var self = this;
return function() {
self.assign(id, value);
};
},
stringEscapeRegex: /[^ a-zA-Z0-9]/g,
stringEscapeFn: function(c) {
return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
},
escape: function(value) {
if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
if (isNumber(value)) return value.toString();
if (value === true) return 'true';
if (value === false) return 'false';
if (value === null) return 'null';
if (typeof value === 'undefined') return 'undefined';
throw $parseMinErr('esc', 'IMPOSSIBLE');
},
nextId: function(skip, init) {
var id = 'v' + (this.state.nextId++);
if (!skip) {
this.current().vars.push(id + (init ? '=' + init : ''));
}
return id;
},
current: function() {
return this.state[this.state.computing];
}
};
function ASTInterpreter(astBuilder, $filter) {
this.astBuilder = astBuilder;
this.$filter = $filter;
}
ASTInterpreter.prototype = {
compile: function(expression, expensiveChecks) {
var self = this;
var ast = this.astBuilder.ast(expression);
this.expression = expression;
this.expensiveChecks = expensiveChecks;
findConstantAndWatchExpressions(ast, self.$filter);
var assignable;
var assign;
if ((assignable = assignableAST(ast))) {
assign = this.recurse(assignable);
}
var toWatch = getInputs(ast.body);
var inputs;
if (toWatch) {
inputs = [];
forEach(toWatch, function(watch, key) {
var input = self.recurse(watch);
watch.input = input;
inputs.push(input);
watch.watchId = key;
});
}
var expressions = [];
forEach(ast.body, function(expression) {
expressions.push(self.recurse(expression.expression));
});
var fn = ast.body.length === 0 ? function() {} :
ast.body.length === 1 ? expressions[0] :
function(scope, locals) {
var lastValue;
forEach(expressions, function(exp) {
lastValue = exp(scope, locals);
});
return lastValue;
};
if (assign) {
fn.assign = function(scope, value, locals) {
return assign(scope, locals, value);
};
}
if (inputs) {
fn.inputs = inputs;
}
fn.literal = isLiteral(ast);
fn.constant = isConstant(ast);
return fn;
},
recurse: function(ast, context, create) {
var left, right, self = this, args, expression;
if (ast.input) {
return this.inputs(ast.input, ast.watchId);
}
switch (ast.type) {
case AST.Literal:
return this.value(ast.value, context);
case AST.UnaryExpression:
right = this.recurse(ast.argument);
return this['unary' + ast.operator](right, context);
case AST.BinaryExpression:
left = this.recurse(ast.left);
right = this.recurse(ast.right);
return this['binary' + ast.operator](left, right, context);
case AST.LogicalExpression:
left = this.recurse(ast.left);
right = this.recurse(ast.right);
return this['binary' + ast.operator](left, right, context);
case AST.ConditionalExpression:
return this['ternary?:'](
this.recurse(ast.test),
this.recurse(ast.alternate),
this.recurse(ast.consequent),
context
);
case AST.Identifier:
ensureSafeMemberName(ast.name, self.expression);
return self.identifier(ast.name,
self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
context, create, self.expression);
case AST.MemberExpression:
left = this.recurse(ast.object, false, !!create);
if (!ast.computed) {
ensureSafeMemberName(ast.property.name, self.expression);
right = ast.property.name;
}
if (ast.computed) right = this.recurse(ast.property);
return ast.computed ?
this.computedMember(left, right, context, create, self.expression) :
this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
case AST.CallExpression:
args = [];
forEach(ast.arguments, function(expr) {
args.push(self.recurse(expr));
});
if (ast.filter) right = this.$filter(ast.callee.name);
if (!ast.filter) right = this.recurse(ast.callee, true);
return ast.filter ?
function(scope, locals, assign, inputs) {
var values = [];
for (var i = 0; i < args.length; ++i) {
values.push(args[i](scope, locals, assign, inputs));
}
var value = right.apply(undefined, values, inputs);
return context ? {context: undefined, name: undefined, value: value} : value;
} :
function(scope, locals, assign, inputs) {
var rhs = right(scope, locals, assign, inputs);
var value;
if (rhs.value != null) {
ensureSafeObject(rhs.context, self.expression);
ensureSafeFunction(rhs.value, self.expression);
var values = [];
for (var i = 0; i < args.length; ++i) {
values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
}
value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
}
return context ? {value: value} : value;
};
case AST.AssignmentExpression:
left = this.recurse(ast.left, true, 1);
right = this.recurse(ast.right);
return function(scope, locals, assign, inputs) {
var lhs = left(scope, locals, assign, inputs);
var rhs = right(scope, locals, assign, inputs);
ensureSafeObject(lhs.value, self.expression);
lhs.context[lhs.name] = rhs;
return context ? {value: rhs} : rhs;
};
case AST.ArrayExpression:
args = [];
forEach(ast.elements, function(expr) {
args.push(self.recurse(expr));
});
return function(scope, locals, assign, inputs) {
var value = [];
for (var i = 0; i < args.length; ++i) {
value.push(args[i](scope, locals, assign, inputs));
}
return context ? {value: value} : value;
};
case AST.ObjectExpression:
args = [];
forEach(ast.properties, function(property) {
args.push({key: property.key.type === AST.Identifier ?
property.key.name :
('' + property.key.value),
value: self.recurse(property.value)
});
});
return function(scope, locals, assign, inputs) {
var value = {};
for (var i = 0; i < args.length; ++i) {
value[args[i].key] = args[i].value(scope, locals, assign, inputs);
}
return context ? {value: value} : value;
};
case AST.ThisExpression:
return function(scope) {
return context ? {value: scope} : scope;
};
case AST.NGValueParameter:
return function(scope, locals, assign, inputs) {
return context ? {value: assign} : assign;
};
}
},
'unary+': function(argument, context) {
return function(scope, locals, assign, inputs) {
var arg = argument(scope, locals, assign, inputs);
if (isDefined(arg)) {
arg = +arg;
} else {
arg = 0;
}
return context ? {value: arg} : arg;
};
},
'unary-': function(argument, context) {
return function(scope, locals, assign, inputs) {
var arg = argument(scope, locals, assign, inputs);
if (isDefined(arg)) {
arg = -arg;
} else {
arg = 0;
}
return context ? {value: arg} : arg;
};
},
'unary!': function(argument, context) {
return function(scope, locals, assign, inputs) {
var arg = !argument(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary+': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var lhs = left(scope, locals, assign, inputs);
var rhs = right(scope, locals, assign, inputs);
var arg = plusFn(lhs, rhs);
return context ? {value: arg} : arg;
};
},
'binary-': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var lhs = left(scope, locals, assign, inputs);
var rhs = right(scope, locals, assign, inputs);
var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
return context ? {value: arg} : arg;
};
},
'binary*': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary/': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary%': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary===': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary!==': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary==': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary!=': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary<': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary>': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary<=': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary>=': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary&&': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'binary||': function(left, right, context) {
return function(scope, locals, assign, inputs) {
var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
'ternary?:': function(test, alternate, consequent, context) {
return function(scope, locals, assign, inputs) {
var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
return context ? {value: arg} : arg;
};
},
value: function(value, context) {
return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
},
identifier: function(name, expensiveChecks, context, create, expression) {
return function(scope, locals, assign, inputs) {
var base = locals && (name in locals) ? locals : scope;
if (create && create !== 1 && base && !(base[name])) {
base[name] = {};
}
var value = base ? base[name] : undefined;
if (expensiveChecks) {
ensureSafeObject(value, expression);
}
if (context) {
return {context: base, name: name, value: value};
} else {
return value;
}
};
},
computedMember: function(left, right, context, create, expression) {
return function(scope, locals, assign, inputs) {
var lhs = left(scope, locals, assign, inputs);
var rhs;
var value;
if (lhs != null) {
rhs = right(scope, locals, assign, inputs);
ensureSafeMemberName(rhs, expression);
if (create && create !== 1 && lhs && !(lhs[rhs])) {
lhs[rhs] = {};
}
value = lhs[rhs];
ensureSafeObject(value, expression);
}
if (context) {
return {context: lhs, name: rhs, value: value};
} else {
return value;
}
};
},
nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
return function(scope, locals, assign, inputs) {
var lhs = left(scope, locals, assign, inputs);
if (create && create !== 1 && lhs && !(lhs[right])) {
lhs[right] = {};
}
var value = lhs != null ? lhs[right] : undefined;
if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
ensureSafeObject(value, expression);
}
if (context) {
return {context: lhs, name: right, value: value};
} else {
return value;
}
};
},
inputs: function(input, watchId) {
return function(scope, value, locals, inputs) {
if (inputs) return inputs[watchId];
return input(scope, value, locals);
};
}
};
/**
* @constructor
*/
var Parser = function(lexer, $filter, options) {
this.lexer = lexer;
this.$filter = $filter;
this.options = options;
this.ast = new AST(this.lexer);
this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
new ASTCompiler(this.ast, $filter);
};
Parser.prototype = {
constructor: Parser,
parse: function(text) {
return this.astCompiler.compile(text, this.options.expensiveChecks);
}
};
//////////////////////////////////////////////////
// Parser helper functions
//////////////////////////////////////////////////
function setter(obj, path, setValue, fullExp) {
ensureSafeObject(obj, fullExp);
var element = path.split('.'), key;
for (var i = 0; element.length > 1; i++) {
key = ensureSafeMemberName(element.shift(), fullExp);
var propertyObj = ensureSafeObject(obj[key], fullExp);
if (!propertyObj) {
propertyObj = {};
obj[key] = propertyObj;
}
obj = propertyObj;
}
key = ensureSafeMemberName(element.shift(), fullExp);
ensureSafeObject(obj[key], fullExp);
obj[key] = setValue;
return setValue;
}
var getterFnCacheDefault = createMap();
var getterFnCacheExpensive = createMap();
function isPossiblyDangerousMemberName(name) {
return name == 'constructor';
}
var objectValueOf = Object.prototype.valueOf;
function getValueOf(value) {
return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
}
///////////////////////////////////
/**
* @ngdoc service
* @name $parse
* @kind function
*
* @description
*
* Converts Angular {@link guide/expression expression} into a function.
*
* ```js
* var getter = $parse('user.name');
* var setter = getter.assign;
* var context = {user:{name:'angular'}};
* var locals = {user:{name:'local'}};
*
* expect(getter(context)).toEqual('angular');
* setter(context, 'newValue');
* expect(context.user.name).toEqual('newValue');
* expect(getter(context, locals)).toEqual('local');
* ```
*
*
* @param {string} expression String expression to compile.
* @returns {function(context, locals)} a function which represents the compiled expression:
*
* * `context` – `{object}` – an object against which any expressions embedded in the strings
* are evaluated against (typically a scope object).
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
* `context`.
*
* The returned function also has the following properties:
* * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
* literal.
* * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
* constant literals.
* * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
* set to a function to change its value on the given context.
*
*/
/**
* @ngdoc provider
* @name $parseProvider
*
* @description
* `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
* service.
*/
function $ParseProvider() {
var cacheDefault = createMap();
var cacheExpensive = createMap();
this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
var $parseOptions = {
csp: $sniffer.csp,
expensiveChecks: false
},
$parseOptionsExpensive = {
csp: $sniffer.csp,
expensiveChecks: true
};
return function $parse(exp, interceptorFn, expensiveChecks) {
var parsedExpression, oneTime, cacheKey;
switch (typeof exp) {
case 'string':
exp = exp.trim();
cacheKey = exp;
var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
parsedExpression = cache[cacheKey];
if (!parsedExpression) {
if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
oneTime = true;
exp = exp.substring(2);
}
var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
var lexer = new Lexer(parseOptions);
var parser = new Parser(lexer, $filter, parseOptions);
parsedExpression = parser.parse(exp);
if (parsedExpression.constant) {
parsedExpression.$$watchDelegate = constantWatchDelegate;
} else if (oneTime) {
parsedExpression.$$watchDelegate = parsedExpression.literal ?
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
} else if (parsedExpression.inputs) {
parsedExpression.$$watchDelegate = inputsWatchDelegate;
}
cache[cacheKey] = parsedExpression;
}
return addInterceptor(parsedExpression, interceptorFn);
case 'function':
return addInterceptor(exp, interceptorFn);
default:
return noop;
}
};
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
if (newValue == null || oldValueOfValue == null) { // null/undefined
return newValue === oldValueOfValue;
}
if (typeof newValue === 'object') {
// attempt to convert the value to a primitive type
// TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
// be cheaply dirty-checked
newValue = getValueOf(newValue);
if (typeof newValue === 'object') {
// objects/arrays are not supported - deep-watching them would be too expensive
return false;
}
// fall-through to the primitive equality check
}
//Primitive or NaN
return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
}
function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
var inputExpressions = parsedExpression.inputs;
var lastResult;
if (inputExpressions.length === 1) {
var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
inputExpressions = inputExpressions[0];
return scope.$watch(function expressionInputWatch(scope) {
var newInputValue = inputExpressions(scope);
if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
oldInputValueOf = newInputValue && getValueOf(newInputValue);
}
return lastResult;
}, listener, objectEquality, prettyPrintExpression);
}
var oldInputValueOfValues = [];
var oldInputValues = [];
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
oldInputValues[i] = null;
}
return scope.$watch(function expressionInputsWatch(scope) {
var changed = false;
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
var newInputValue = inputExpressions[i](scope);
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
oldInputValues[i] = newInputValue;
oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
}
}
if (changed) {
lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
}
return lastResult;
}, listener, objectEquality, prettyPrintExpression);
}
function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
var unwatch, lastValue;
return unwatch = scope.$watch(function oneTimeWatch(scope) {
return parsedExpression(scope);
}, function oneTimeListener(value, old, scope) {
lastValue = value;
if (isFunction(listener)) {
listener.apply(this, arguments);
}
if (isDefined(value)) {
scope.$$postDigest(function() {
if (isDefined(lastValue)) {
unwatch();
}
});
}
}, objectEquality);
}
function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
var unwatch, lastValue;
return unwatch = scope.$watch(function oneTimeWatch(scope) {
return parsedExpression(scope);
}, function oneTimeListener(value, old, scope) {
lastValue = value;
if (isFunction(listener)) {
listener.call(this, value, old, scope);
}
if (isAllDefined(value)) {
scope.$$postDigest(function() {
if (isAllDefined(lastValue)) unwatch();
});
}
}, objectEquality);
function isAllDefined(value) {
var allDefined = true;
forEach(value, function(val) {
if (!isDefined(val)) allDefined = false;
});
return allDefined;
}
}
function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
var unwatch;
return unwatch = scope.$watch(function constantWatch(scope) {
return parsedExpression(scope);
}, function constantListener(value, old, scope) {
if (isFunction(listener)) {
listener.apply(this, arguments);
}
unwatch();
}, objectEquality);
}
function addInterceptor(parsedExpression, interceptorFn) {
if (!interceptorFn) return parsedExpression;
var watchDelegate = parsedExpression.$$watchDelegate;
var regularWatch =
watchDelegate !== oneTimeLiteralWatchDelegate &&
watchDelegate !== oneTimeWatchDelegate;
var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
var value = parsedExpression(scope, locals, assign, inputs);
return interceptorFn(value, scope, locals);
} : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
var value = parsedExpression(scope, locals, assign, inputs);
var result = interceptorFn(value, scope, locals);
// we only return the interceptor's result if the
// initial value is defined (for bind-once)
return isDefined(value) ? result : value;
};
// Propagate $$watchDelegates other then inputsWatchDelegate
if (parsedExpression.$$watchDelegate &&
parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
fn.$$watchDelegate = parsedExpression.$$watchDelegate;
} else if (!interceptorFn.$stateful) {
// If there is an interceptor, but no watchDelegate then treat the interceptor like
// we treat filters - it is assumed to be a pure function unless flagged with $stateful
fn.$$watchDelegate = inputsWatchDelegate;
fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
}
return fn;
}
}];
}
/**
* @ngdoc service
* @name $q
* @requires $rootScope
*
* @description
* A service that helps you run functions asynchronously, and use their return values (or exceptions)
* when they are done processing.
*
* This is an implementation of promises/deferred objects inspired by
* [Kris Kowal's Q](https://github.com/kriskowal/q).
*
* $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
* implementations, and the other which resembles ES6 promises to some degree.
*
* # $q constructor
*
* The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
* function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
* see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
*
* While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
* available yet.
*
* It can be used like so:
*
* ```js
* // for the purpose of this example let's assume that variables `$q` and `okToGreet`
* // are available in the current lexical scope (they could have been injected or passed in).
*
* function asyncGreet(name) {
* // perform some asynchronous operation, resolve or reject the promise when appropriate.
* return $q(function(resolve, reject) {
* setTimeout(function() {
* if (okToGreet(name)) {
* resolve('Hello, ' + name + '!');
* } else {
* reject('Greeting ' + name + ' is not allowed.');
* }
* }, 1000);
* });
* }
*
* var promise = asyncGreet('Robin Hood');
* promise.then(function(greeting) {
* alert('Success: ' + greeting);
* }, function(reason) {
* alert('Failed: ' + reason);
* });
* ```
*
* Note: progress/notify callbacks are not currently supported via the ES6-style interface.
*
* However, the more traditional CommonJS-style usage is still available, and documented below.
*
* [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
* interface for interacting with an object that represents the result of an action that is
* performed asynchronously, and may or may not be finished at any given point in time.
*
* From the perspective of dealing with error handling, deferred and promise APIs are to
* asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
*
* ```js
* // for the purpose of this example let's assume that variables `$q` and `okToGreet`
* // are available in the current lexical scope (they could have been injected or passed in).
*
* function asyncGreet(name) {
* var deferred = $q.defer();
*
* setTimeout(function() {
* deferred.notify('About to greet ' + name + '.');
*
* if (okToGreet(name)) {
* deferred.resolve('Hello, ' + name + '!');
* } else {
* deferred.reject('Greeting ' + name + ' is not allowed.');
* }
* }, 1000);
*
* return deferred.promise;
* }
*
* var promise = asyncGreet('Robin Hood');
* promise.then(function(greeting) {
* alert('Success: ' + greeting);
* }, function(reason) {
* alert('Failed: ' + reason);
* }, function(update) {
* alert('Got notification: ' + update);
* });
* ```
*
* At first it might not be obvious why this extra complexity is worth the trouble. The payoff
* comes in the way of guarantees that promise and deferred APIs make, see
* https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
*
* Additionally the promise api allows for composition that is very hard to do with the
* traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
* For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
* section on serial or parallel joining of promises.
*
* # The Deferred API
*
* A new instance of deferred is constructed by calling `$q.defer()`.
*
* The purpose of the deferred object is to expose the associated Promise instance as well as APIs
* that can be used for signaling the successful or unsuccessful completion, as well as the status
* of the task.
*
* **Methods**
*
* - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
* constructed via `$q.reject`, the promise will be rejected instead.
* - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
* resolving it with a rejection constructed via `$q.reject`.
* - `notify(value)` - provides updates on the status of the promise's execution. This may be called
* multiple times before the promise is either resolved or rejected.
*
* **Properties**
*
* - promise – `{Promise}` – promise object associated with this deferred.
*
*
* # The Promise API
*
* A new promise instance is created when a deferred instance is created and can be retrieved by
* calling `deferred.promise`.
*
* The purpose of the promise object is to allow for interested parties to get access to the result
* of the deferred task when it completes.
*
* **Methods**
*
* - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
* will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
* as soon as the result is available. The callbacks are called with a single argument: the result
* or rejection reason. Additionally, the notify callback may be called zero or more times to
* provide a progress indication, before the promise is resolved or rejected.
*
* This method *returns a new promise* which is resolved or rejected via the return value of the
* `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
* with the value which is resolved in that promise using
* [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
* It also notifies via the return value of the `notifyCallback` method. The promise cannot be
* resolved or rejected from the notifyCallback method.
*
* - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
*
* - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
* but to do so without modifying the final value. This is useful to release resources or do some
* clean-up that needs to be done whether the promise was rejected or resolved. See the [full
* specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
* more information.
*
* # Chaining promises
*
* Because calling the `then` method of a promise returns a new derived promise, it is easily
* possible to create a chain of promises:
*
* ```js
* promiseB = promiseA.then(function(result) {
* return result + 1;
* });
*
* // promiseB will be resolved immediately after promiseA is resolved and its value
* // will be the result of promiseA incremented by 1
* ```
*
* It is possible to create chains of any length and since a promise can be resolved with another
* promise (which will defer its resolution further), it is possible to pause/defer resolution of
* the promises at any point in the chain. This makes it possible to implement powerful APIs like
* $http's response interceptors.
*
*
* # Differences between Kris Kowal's Q and $q
*
* There are two main differences:
*
* - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
* mechanism in angular, which means faster propagation of resolution or rejection into your
* models and avoiding unnecessary browser repaints, which would result in flickering UI.
* - 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
*
* ```js
* it('should simulate promise', inject(function($q, $rootScope) {
* var deferred = $q.defer();
* var promise = deferred.promise;
* var resolvedValue;
*
* promise.then(function(value) { resolvedValue = value; });
* expect(resolvedValue).toBeUndefined();
*
* // Simulate resolving of promise
* deferred.resolve(123);
* // Note that the 'then' function does not get called synchronously.
* // This is because we want the promise API to always be async, whether or not
* // it got called synchronously or asynchronously.
* expect(resolvedValue).toBeUndefined();
*
* // Propagate promise resolution to 'then' functions using $apply().
* $rootScope.$apply();
* expect(resolvedValue).toEqual(123);
* }));
* ```
*
* @param {function(function, function)} resolver Function which is responsible for resolving or
* rejecting the newly created promise. The first parameter is a function which resolves the
* promise, the second parameter is a function which rejects the promise.
*
* @returns {Promise} The newly created promise.
*/
function $QProvider() {
this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
return qFactory(function(callback) {
$rootScope.$evalAsync(callback);
}, $exceptionHandler);
}];
}
function $$QProvider() {
this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
return qFactory(function(callback) {
$browser.defer(callback);
}, $exceptionHandler);
}];
}
/**
* Constructs a promise manager.
*
* @param {function(function)} nextTick Function for executing functions in the next turn.
* @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
* debugging purposes.
* @returns {object} Promise manager.
*/
function qFactory(nextTick, exceptionHandler) {
var $qMinErr = minErr('$q', TypeError);
function callOnce(self, resolveFn, rejectFn) {
var called = false;
function wrap(fn) {
return function(value) {
if (called) return;
called = true;
fn.call(self, value);
};
}
return [wrap(resolveFn), wrap(rejectFn)];
}
/**
* @ngdoc method
* @name ng.$q#defer
* @kind function
*
* @description
* Creates a `Deferred` object which represents a task which will finish in the future.
*
* @returns {Deferred} Returns a new instance of deferred.
*/
var defer = function() {
return new Deferred();
};
function Promise() {
this.$$state = { status: 0 };
}
Promise.prototype = {
then: function(onFulfilled, onRejected, progressBack) {
var result = new Deferred();
this.$$state.pending = this.$$state.pending || [];
this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
return result.promise;
},
"catch": function(callback) {
return this.then(null, callback);
},
"finally": function(callback, progressBack) {
return this.then(function(value) {
return handleCallback(value, true, callback);
}, function(error) {
return handleCallback(error, false, callback);
}, progressBack);
}
};
//Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
function simpleBind(context, fn) {
return function(value) {
fn.call(context, value);
};
}
function processQueue(state) {
var fn, deferred, pending;
pending = state.pending;
state.processScheduled = false;
state.pending = undefined;
for (var i = 0, ii = pending.length; i < ii; ++i) {
deferred = pending[i][0];
fn = pending[i][state.status];
try {
if (isFunction(fn)) {
deferred.resolve(fn(state.value));
} else if (state.status === 1) {
deferred.resolve(state.value);
} else {
deferred.reject(state.value);
}
} catch (e) {
deferred.reject(e);
exceptionHandler(e);
}
}
}
function scheduleProcessQueue(state) {
if (state.processScheduled || !state.pending) return;
state.processScheduled = true;
nextTick(function() { processQueue(state); });
}
function Deferred() {
this.promise = new Promise();
//Necessary to support unbound execution :/
this.resolve = simpleBind(this, this.resolve);
this.reject = simpleBind(this, this.reject);
this.notify = simpleBind(this, this.notify);
}
Deferred.prototype = {
resolve: function(val) {
if (this.promise.$$state.status) return;
if (val === this.promise) {
this.$$reject($qMinErr(
'qcycle',
"Expected promise to be resolved with value other than itself '{0}'",
val));
} else {
this.$$resolve(val);
}
},
$$resolve: function(val) {
var then, fns;
fns = callOnce(this, this.$$resolve, this.$$reject);
try {
if ((isObject(val) || isFunction(val))) then = val && val.then;
if (isFunction(then)) {
this.promise.$$state.status = -1;
then.call(val, fns[0], fns[1], this.notify);
} else {
this.promise.$$state.value = val;
this.promise.$$state.status = 1;
scheduleProcessQueue(this.promise.$$state);
}
} catch (e) {
fns[1](e);
exceptionHandler(e);
}
},
reject: function(reason) {
if (this.promise.$$state.status) return;
this.$$reject(reason);
},
$$reject: function(reason) {
this.promise.$$state.value = reason;
this.promise.$$state.status = 2;
scheduleProcessQueue(this.promise.$$state);
},
notify: function(progress) {
var callbacks = this.promise.$$state.pending;
if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
nextTick(function() {
var callback, result;
for (var i = 0, ii = callbacks.length; i < ii; i++) {
result = callbacks[i][0];
callback = callbacks[i][3];
try {
result.notify(isFunction(callback) ? callback(progress) : progress);
} catch (e) {
exceptionHandler(e);
}
}
});
}
}
};
/**
* @ngdoc method
* @name $q#reject
* @kind function
*
* @description
* Creates a promise that is resolved as rejected with the specified `reason`. This api should be
* used to forward rejection in a chain of promises. If you are dealing with the last promise in
* a promise chain, you don't need to worry about it.
*
* When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
* `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
* a promise error callback and you want to forward the error to the promise derived from the
* current promise, you have to "rethrow" the error by returning a rejection constructed via
* `reject`.
*
* ```js
* promiseB = promiseA.then(function(result) {
* // success: do something and resolve promiseB
* // with the old or a new result
* return result;
* }, function(reason) {
* // error: handle the error if possible and
* // resolve promiseB with newPromiseOrValue,
* // otherwise forward the rejection to promiseB
* if (canHandle(reason)) {
* // handle the error and recover
* return newPromiseOrValue;
* }
* return $q.reject(reason);
* });
* ```
*
* @param {*} reason Constant, message, exception or an object representing the rejection reason.
* @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
*/
var reject = function(reason) {
var result = new Deferred();
result.reject(reason);
return result.promise;
};
var makePromise = function makePromise(value, resolved) {
var result = new Deferred();
if (resolved) {
result.resolve(value);
} else {
result.reject(value);
}
return result.promise;
};
var handleCallback = function handleCallback(value, isResolved, callback) {
var callbackOutput = null;
try {
if (isFunction(callback)) callbackOutput = callback();
} catch (e) {
return makePromise(e, false);
}
if (isPromiseLike(callbackOutput)) {
return callbackOutput.then(function() {
return makePromise(value, isResolved);
}, function(error) {
return makePromise(error, false);
});
} else {
return makePromise(value, isResolved);
}
};
/**
* @ngdoc method
* @name $q#when
* @kind function
*
* @description
* Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
* This is useful when you are dealing with an object that might or might not be a promise, or if
* the promise comes from a source that can't be trusted.
*
* @param {*} value Value or a promise
* @returns {Promise} Returns a promise of the passed value or promise
*/
var when = function(value, callback, errback, progressBack) {
var result = new Deferred();
result.resolve(value);
return result.promise.then(callback, errback, progressBack);
};
/**
* @ngdoc method
* @name $q#all
* @kind function
*
* @description
* Combines multiple promises into a single promise that is resolved when all of the input
* promises are resolved.
*
* @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
* @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
* each value corresponding to the promise at the same index/key in the `promises` array/hash.
* If any of the promises is resolved with a rejection, this resulting promise will be rejected
* with the same rejection value.
*/
function all(promises) {
var deferred = new Deferred(),
counter = 0,
results = isArray(promises) ? [] : {};
forEach(promises, function(promise, key) {
counter++;
when(promise).then(function(value) {
if (results.hasOwnProperty(key)) return;
results[key] = value;
if (!(--counter)) deferred.resolve(results);
}, function(reason) {
if (results.hasOwnProperty(key)) return;
deferred.reject(reason);
});
});
if (counter === 0) {
deferred.resolve(results);
}
return deferred.promise;
}
var $Q = function Q(resolver) {
if (!isFunction(resolver)) {
throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
}
if (!(this instanceof Q)) {
// More useful when $Q is the Promise itself.
return new Q(resolver);
}
var deferred = new Deferred();
function resolveFn(value) {
deferred.resolve(value);
}
function rejectFn(reason) {
deferred.reject(reason);
}
resolver(resolveFn, rejectFn);
return deferred.promise;
};
$Q.defer = defer;
$Q.reject = reject;
$Q.when = when;
$Q.all = all;
return $Q;
}
function $$RAFProvider() { //rAF
this.$get = ['$window', '$timeout', function($window, $timeout) {
var requestAnimationFrame = $window.requestAnimationFrame ||
$window.webkitRequestAnimationFrame;
var cancelAnimationFrame = $window.cancelAnimationFrame ||
$window.webkitCancelAnimationFrame ||
$window.webkitCancelRequestAnimationFrame;
var rafSupported = !!requestAnimationFrame;
var rafFn = rafSupported
? function(fn) {
var id = requestAnimationFrame(fn);
return function() {
cancelAnimationFrame(id);
};
}
: function(fn) {
var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
return function() {
$timeout.cancel(timer);
};
};
queueFn.supported = rafSupported;
var cancelLastRAF;
var taskCount = 0;
var taskQueue = [];
return queueFn;
function flush() {
for (var i = 0; i < taskQueue.length; i++) {
var task = taskQueue[i];
if (task) {
taskQueue[i] = null;
task();
}
}
taskCount = taskQueue.length = 0;
}
function queueFn(asyncFn) {
var index = taskQueue.length;
taskCount++;
taskQueue.push(asyncFn);
if (index === 0) {
cancelLastRAF = rafFn(flush);
}
return function cancelQueueFn() {
if (index >= 0) {
taskQueue[index] = null;
index = null;
if (--taskCount === 0 && cancelLastRAF) {
cancelLastRAF();
cancelLastRAF = null;
taskQueue.length = 0;
}
}
};
}
}];
}
/**
* DESIGN NOTES
*
* The design decisions behind the scope are heavily favored for speed and memory consumption.
*
* The typical use of scope is to watch the expressions, which most of the time return the same
* value as last time so we optimize the operation.
*
* Closures construction is expensive in terms of speed as well as memory:
* - No closures, instead use prototypical inheritance for API
* - Internal state needs to be stored on scope directly, which means that private state is
* exposed as $$____ properties
*
* Loop operations are optimized by using while(count--) { ... }
* - this means that in order to keep the same order of execution as addition we have to add
* items to the array at the beginning (unshift) instead of at the end (push)
*
* Child scopes are created and removed often
* - Using an array would be slow since inserts in middle are expensive so we use linked list
*
* There are few watches then a lot of observers. This is why you don't want the observer to be
* implemented in the same way as watch. Watch requires return of initialization function which
* are expensive to construct.
*/
/**
* @ngdoc provider
* @name $rootScopeProvider
* @description
*
* Provider for the $rootScope service.
*/
/**
* @ngdoc method
* @name $rootScopeProvider#digestTtl
* @description
*
* Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
* assuming that the model is unstable.
*
* The current default is 10 iterations.
*
* In complex applications it's possible that the dependencies between `$watch`s will result in
* several digest iterations. However if an application needs more than the default 10 digest
* iterations for its model to stabilize then you should investigate what is causing the model to
* continuously change during the digest.
*
* Increasing the TTL could have performance implications, so you should not change it without
* proper justification.
*
* @param {number} limit The number of digest iterations.
*/
/**
* @ngdoc service
* @name $rootScope
* @description
*
* Every application has a single root {@link ng.$rootScope.Scope scope}.
* All other scopes are descendant scopes of the root scope. Scopes provide separation
* between the model and the view, via a mechanism for watching the model for changes.
* They also provide an event emission/broadcast and subscription facility. See the
* {@link guide/scope developer guide on scopes}.
*/
function $RootScopeProvider() {
var TTL = 10;
var $rootScopeMinErr = minErr('$rootScope');
var lastDirtyWatch = null;
var applyAsyncId = null;
this.digestTtl = function(value) {
if (arguments.length) {
TTL = value;
}
return TTL;
};
function createChildScopeClass(parent) {
function ChildScope() {
this.$$watchers = this.$$nextSibling =
this.$$childHead = this.$$childTail = null;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$id = nextUid();
this.$$ChildScope = null;
}
ChildScope.prototype = parent;
return ChildScope;
}
this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
function($injector, $exceptionHandler, $parse, $browser) {
function destroyChildScope($event) {
$event.currentScope.$$destroyed = true;
}
/**
* @ngdoc type
* @name $rootScope.Scope
*
* @description
* A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
* {@link auto.$injector $injector}. Child scopes are created using the
* {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
* compiled HTML template is executed.)
*
* Here is a simple scope snippet to show how you can interact with the scope.
* ```html
* <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
* ```
*
* # Inheritance
* A scope can inherit from a parent scope, as in this example:
* ```js
var parent = $rootScope;
var child = parent.$new();
parent.salutation = "Hello";
expect(child.salutation).toEqual('Hello');
child.salutation = "Welcome";
expect(child.salutation).toEqual('Welcome');
expect(parent.salutation).toEqual('Hello');
* ```
*
* When interacting with `Scope` in tests, additional helper methods are available on the
* instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
* details.
*
*
* @param {Object.<string, function()>=} providers Map of service factory which need to be
* provided for the current scope. Defaults to {@link ng}.
* @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
* append/override services provided by `providers`. This is handy
* when unit-testing and having the need to override a default
* service.
* @returns {Object} Newly created scope.
*
*/
function Scope() {
this.$id = nextUid();
this.$$phase = this.$parent = this.$$watchers =
this.$$nextSibling = this.$$prevSibling =
this.$$childHead = this.$$childTail = null;
this.$root = this;
this.$$destroyed = false;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$$isolateBindings = null;
}
/**
* @ngdoc property
* @name $rootScope.Scope#$id
*
* @description
* Unique scope ID (monotonically increasing) useful for debugging.
*/
/**
* @ngdoc property
* @name $rootScope.Scope#$parent
*
* @description
* Reference to the parent scope.
*/
/**
* @ngdoc property
* @name $rootScope.Scope#$root
*
* @description
* Reference to the root scope.
*/
Scope.prototype = {
constructor: Scope,
/**
* @ngdoc method
* @name $rootScope.Scope#$new
* @kind function
*
* @description
* Creates a new child {@link ng.$rootScope.Scope scope}.
*
* The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
* The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
*
* {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
* desired for the scope and its child scopes to be permanently detached from the parent and
* thus stop participating in model change detection and listener notification by invoking.
*
* @param {boolean} isolate If true, then the scope does not prototypically inherit from the
* parent scope. The scope is isolated, as it can not see parent scope properties.
* When creating widgets, it is useful for the widget to not accidentally read parent
* state.
*
* @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
* of the newly created scope. Defaults to `this` scope if not provided.
* This is used when creating a transclude scope to correctly place it
* in the scope hierarchy while maintaining the correct prototypical
* inheritance.
*
* @returns {Object} The newly created child scope.
*
*/
$new: function(isolate, parent) {
var child;
parent = parent || this;
if (isolate) {
child = new Scope();
child.$root = this.$root;
} else {
// Only create a child scope class if somebody asks for one,
// but cache it to allow the VM to optimize lookups.
if (!this.$$ChildScope) {
this.$$ChildScope = createChildScopeClass(this);
}
child = new this.$$ChildScope();
}
child.$parent = parent;
child.$$prevSibling = parent.$$childTail;
if (parent.$$childHead) {
parent.$$childTail.$$nextSibling = child;
parent.$$childTail = child;
} else {
parent.$$childHead = parent.$$childTail = child;
}
// When the new scope is not isolated or we inherit from `this`, and
// the parent scope is destroyed, the property `$$destroyed` is inherited
// prototypically. In all other cases, this property needs to be set
// when the parent scope is destroyed.
// The listener needs to be added after the parent is set
if (isolate || parent != this) child.$on('$destro
View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment