rolodex 2.0.2 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/LICENSE.txt +1 -1
  4. data/bower.json +1 -2
  5. data/dist/rolodex.js +1747 -0
  6. data/dist/rolodex.min.js +1 -0
  7. data/gulpfile.js +57 -0
  8. data/lib/rolodex/version.rb +1 -1
  9. data/package.json +25 -14
  10. data/vendor/assets/javascripts/rolodex_angular/src/dropdown.coffee +5 -1
  11. data/vendor/assets/javascripts/rolodex_angular/src/position.coffee +0 -1
  12. data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion-group.ngt +12 -0
  13. data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion.ngt +1 -0
  14. data/vendor/assets/javascripts/rolodex_angular/template/alert/alert.ngt +4 -0
  15. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/datepicker.ngt +5 -0
  16. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/day.ngt +27 -0
  17. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/month.ngt +30 -0
  18. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/popup.ngt +3 -0
  19. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/year.ngt +30 -0
  20. data/vendor/assets/javascripts/rolodex_angular/template/modal/window.ngt +3 -0
  21. data/vendor/assets/stylesheets/rolodex/components/_forms.sass +1 -0
  22. data/vendor/assets/stylesheets/rolodex/settings/mixins/_svg.sass +3 -1
  23. data/vendor/assets/stylesheets/rolodex/settings/mixins/_typography.sass +2 -4
  24. data/vendor/assets/stylesheets/rolodex/settings/variables/_misc.scss +4 -0
  25. data/vendor/assets/stylesheets/rolodex/settings/variables/_typography.scss +1 -1
  26. metadata +16 -25
  27. data/spec/javascripts/rolodex_angular/modal.spec.coffee +0 -13
  28. data/spec/spec_helper.rb +0 -0
  29. data/spec/test_lib/angular-mocks.js +0 -2173
  30. data/spec/test_lib/angular.js +0 -21883
  31. data/spec/test_lib/jquery-2.1.1.js +0 -9190
  32. data/spec/test_lib/lodash.js +0 -6785
  33. data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion-group.ngt.haml +0 -7
  34. data/vendor/assets/javascripts/rolodex_angular/template/accordion/accordion.ngt.haml +0 -1
  35. data/vendor/assets/javascripts/rolodex_angular/template/alert/alert.ngt.haml +0 -3
  36. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/datepicker.ngt.haml +0 -4
  37. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/day.ngt.haml +0 -20
  38. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/month.ngt.haml +0 -17
  39. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/popup.ngt.haml +0 -2
  40. data/vendor/assets/javascripts/rolodex_angular/template/datepicker/year.ngt.haml +0 -17
  41. data/vendor/assets/javascripts/rolodex_angular/template/modal/window.ngt.haml +0 -2
@@ -0,0 +1 @@
1
+ (function(){angular.module("rolodex",["templates","rolodex.accordion","rolodex.alert","rolodex.buttons","rolodex.datepicker","rolodex.dropdown","rolodex.modal","rolodex.transition"])}).call(this),angular.module("rolodex").run(["$templateCache",function(e){e.put("rolodex_angular/template/accordion/accordion-group",'<div class="panel panel-default"><div class="panel-heading"><h4 class="panel-title"><a accordion-transclude="heading" class="accordion-toggle" ng-click="toggleOpen()"><span ng-class="{\'text-muted\': isDisabled}">{{heading}}</span></a></h4></div><div class="panel-collapse" collapse="!isOpen"><div class="panel-body" ng-transclude=""></div></div></div>'),e.put("rolodex_angular/template/accordion/accordion",'<div class="panel-group" ng-transclude=""></div>'),e.put("rolodex_angular/template/alert/alert",'<div ng-class="[\'alert-banner-\' + (type || \'warning\'), closeable ? \'alert-dismissable\' : null]" role="alert"><button aria-hidden="true" class="i-close" ng-click="close()" ng-show="closeable" type="button">&times;</button><div ng-transclude=""></div></div>'),e.put("rolodex_angular/template/datepicker/datepicker",'<div ng-keydown="keydown($event)" ng-switch="datepickerMode" role="application"><daypicker ng-switch-when="day" tabindex="0"></daypicker><monthpicker ng-switch-when="month" tabindex="0"></monthpicker><yearpicker ng-switch-when="year" tabindex="0"></yearpicker></div>'),e.put("rolodex_angular/template/datepicker/day",'<table aria-activedescendant="{{ activeDateId }}" aria-labelledby="{{uniqueId }}-title" role="grid"><thead><tr><th class="prev available" ng-click="move(-1)" tabindex="-1"><div class="i-caret"></div></th><th class="month" colspan="{{ 5 + showWeeks }}">{{ title }}</th><th class="next available" ng-click="move(1)" tabindex="-1"><div class="i-caret"></div></th></tr><tr><th class="text-center" ng-repeat="label in labels track by $index"><small aria-label="{{ label.full}}">{{ label.abbr }}</small></th></tr></thead><tbody><tr ng-repeat="row in rows track by $index"><td aria-disabled="{{ !!dt.disabled }}" id="{{ dt.uid }}" ng-class="{active: dt.selected, available: !dt.disabled, off: dt.disabled}" ng-click="select(dt.date, dt.disabled)" ng-disabled="dt.disabled" ng-repeat="dt in row track by dt.date" role="gridcell" tabindex="-1"><span ng-class="{\'text-muted\': dt.secondary}">{{ dt.label }}</span></td></tr></tbody></table>'),e.put("rolodex_angular/template/datepicker/month",'<table aria-activedescendant="{{ activeDateId }}" aria-labelledby="{{ uniqueId }}-title" role="grid"><thead><tr><th><button class="btn btn-boring pull-left" ng-click="move(-1)" tabindex="-1" type="button"><i class="i-caret"></i></button></th><th><button aria-atomic="true" aria-live="assertive" class="btn btn-boring" id="{{ uniqueId }}-title" ng-click="toggleMode()" role="heading" style="width:100%;" tabindex="-1" type="button"><strong>{{ title }}</strong></button></th><th><button class="btn btn-boring pull-right" ng-click="move(1)" tabindex="-1" type="button"><i class="i-caret"></i></button></th></tr></thead><tbody><tr ng-repeat="row in rows track by $index"><td aria-disabled="{{ !!dt.disabled }}" class="text-center" id="{{ dt.uid }}" ng-repeat="dt in row track by dt.date" role="gridcell"><button class="btn btn-boring" ng-class="{\'btn-info\': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" style="width:100%;" tabindex="-1" type="button"><span ng-class="{\'text-info\': dt.current}">{{ dt.label }}</span></button></td></tr></tbody></table>'),e.put("rolodex_angular/template/datepicker/popup",'<div class="date-picker-solo daterangepicker dropdown-menu group" data-dropdown-content="" ng-keydown="keydown($event)"><div ng-transclude=""></div></div>'),e.put("rolodex_angular/template/datepicker/year",'<table aria-activedescendant="{{ activeDateId }}" aria-labelledby="{{ uniqueId }}-title" role="grid"><thead><tr><th><button class="btn btn-boring pull-left" ng-click="move(-1)" tabindex="-1" type="button"><i class="i-caret"></i></button></th><th colspan="3"><button aria-atomic="true" aria-live="assertive" class="btn btn-boring" id="{{ uniqueId }}-title" ng-click="toggleMode()" role="heading" style="width:100%;" tabindex="-1" type="button"><strong>{{ title }}</strong></button></th><th><button class="btn btn-boring pull-right" ng-click="move(1)" tabindex="-1" type="button"><i class="i-caret"></i></button></th></tr></thead><tbody><tr ng-repeat="row in rows track by $index"><td aria-disabled="{{ !!dt.disabled }}" class="text-center" id="{{ dt.uid }}" ng-repeat="dt in row track by dt.date" role="gridcell"><button class="btn btn-boring" ng-class="{\'btn-info\': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" style="width:100%;" tabindex="-1" type="button"><span ng-class="{\'text-info\': dt.current}">{{ dt.label }}</span></button></td></tr></tbody></table>'),e.put("rolodex_angular/template/modal/window",'<div class="modal" ng-class="{\'modal-fade\': animate}" ng-click="close($event)" role="dialog" tabindex="-1"><div class="modal-content" modal-transclude=""></div></div>')}]),function(){angular.module("templates",[])}.call(this),function(){angular.module("rolodex.accordion",["rolodex.collapse"]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(e,t,n){return this.groups=[],this.closeOthers=function(r){var a;return a=angular.isDefined(t.closeOthers)?e.$eval(t.closeOthers):n.closeOthers,a?angular.forEach(this.groups,function(e){return e!==r?e.isOpen=!1:void 0}):void 0},this.addGroup=function(e){var t;return t=this,this.groups.push(e),e.$on("$destroy",function(n){return t.removeGroup(e)})},this.removeGroup=function(e){var t;return t=this.groups.indexOf(e),-1!==t?this.groups.splice(t,1):void 0},this}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"rolodex_angular/template/accordion/accordion"}}).directive("accordionGroup",function(){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"rolodex_angular/template/accordion/accordion-group",scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){return this.setHeading=function(e){return this.heading=e}},link:function(e,t,n,r){return r.addGroup(e),e.$watch("isOpen",function(t){t&&r.closeOthers(e)}),e.toggleOpen=function(){return e.isDisabled?void 0:e.isOpen=!e.isOpen}}}}).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(e,t,n,r,a){return r.setHeading(a(e,function(){}))}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(e,t,n,r){return e.$watch(function(){return r[n.accordionTransclude]},function(e){return e?(t.html(""),t.append(e)):void 0})}}})}.call(this),function(){angular.module("rolodex.alert",[]).controller("AlertController",["$scope","$attrs",function(e,t){return e.closeable="close"in t}]).directive("alert",function(){return{restrict:"EA",controller:"AlertController",templateUrl:"rolodex_angular/template/alert/alert",transclude:!0,replace:!0,scope:{type:"@",close:"&"}}})}.call(this),function(){angular.module("rolodex.buttons",[]).constant("buttonConfig",{activeClass:"active",toggleEvent:"click"}).controller("ButtonsController",["buttonConfig",function(e){return this.activeClass=e.activeClass||"active",this.toggleEvent=e.toggleEvent||"click",this}]).directive("btnRadio",function(){return{require:["btnRadio","ngModel"],controller:"ButtonsController",link:function(e,t,n,r){var a,o;return a=r[0],o=r[1],o.$render=function(){return t.toggleClass(a.activeClass,angular.equals(o.$modelValue,e.$eval(n.btnRadio)))},t.bind(a.toggleEvent,function(){var r;return r=t.hasClass(a.activeClass),!r||angular.isDefined(n.uncheckable)?e.$apply(function(){return o.$setViewValue(r?null:e.$eval(n.btnRadio)),o.$render()}):void 0})}}}).directive("btnCheckbox",function(){return{require:["btnCheckbox","ngModel"],controller:"ButtonsController",link:function(e,t,n,r){var a,o,i,l,d;return l=function(){return o(n.btnCheckboxTrue,!0)},i=function(){return o(n.btnCheckboxFalse,!1)},o=function(t,n){var r;return r=e.$eval(t),angular.isDefined(r)?r:n},a=r[0],d=r[1],d.$render=function(){return t.toggleClass(a.activeClass,angular.equals(d.$modelValue,l()))},t.bind(a.toggleEvent,function(){return e.$apply(function(){return d.$setViewValue(t.hasClass(a.activeClass)?i():l()),d.$render()})})}}})}.call(this),function(){angular.module("rolodex.collapse",["rolodex.transition"]).directive("collapse",["$transition",function(e){return{link:function(t,n,r){var a,o,i,l,d,c,s;l=function(t){var r,a,o;return o=function(){var e;return e===a?e=void 0:void 0},a=e(n,t),r&&r.cancel(),r=a,a.then(o,o),a},d=function(){var e;e?(e=!1,c()):(n.removeClass("collapse").addClass("collapsing"),l({height:n[0].scrollHeight+"px"}).then(c))},c=function(){n.removeClass("collapsing"),n.addClass("collapse in"),n.css({height:"auto"})},a=function(){var e,t;e?(e=!1,o(),n.css({height:0})):(n.css({height:n[0].scrollHeight+"px"}),t=n[0].offsetWidth,n.removeClass("collapse in").addClass("collapsing"),l({height:0}).then(o))},o=function(){n.removeClass("collapsing"),n.addClass("collapse")},s=!0,i=void 0,t.$watch(r.collapse,function(e){e?a():d()})}}}])}.call(this),function(){angular.module("rolodex.dateparser",[]).service("dateParser",["$locale","orderByFilter",function(e,t){var n,r,a;return n=function(e){var n,a;return n=[],a=e.split(""),angular.forEach(r,function(t,r){var o,i,l;if(i=e.indexOf(r),i>-1){for(e=e.split(""),a[i]="("+t.regex+")",e[i]="$",o=i+1,l=i+r.length;l>o;)a[o]="",e[o]="$",o++;e=e.join(""),n.push({index:i,apply:t.apply})}}),{regex:new RegExp("^"+a.join("")+"$"),map:t(n,"index")}},a=function(e,t,n){return 1===t&&n>28?29===n&&(e%4===0&&e%100!==0||e%400===0):3===t||5===t||8===t||10===t?31>n:!0},this.parsers={},r={yyyy:{regex:"\\d{4}",apply:function(e){this.year=+e}},yy:{regex:"\\d{2}",apply:function(e){this.year=+e+2e3}},y:{regex:"\\d{1,4}",apply:function(e){this.year=+e}},MMMM:{regex:e.DATETIME_FORMATS.MONTH.join("|"),apply:function(t){this.month=e.DATETIME_FORMATS.MONTH.indexOf(t)}},MMM:{regex:e.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(t){this.month=e.DATETIME_FORMATS.SHORTMONTH.indexOf(t)}},MM:{regex:"0[1-9]|1[0-2]",apply:function(e){this.month=e-1}},M:{regex:"[1-9]|1[0-2]",apply:function(e){this.month=e-1}},dd:{regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(e){this.date=+e}},d:{regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(e){return this.date=+e}},EEEE:{regex:e.DATETIME_FORMATS.DAY.join("|")},EEE:{regex:e.DATETIME_FORMATS.SHORTDAY.join("|")}},this.parse=function(t,r){var o,i,l,d,c,s,u,p,f;if(!angular.isString(t)||!r)return t;if(r=e.DATETIME_FORMATS[r]||r,this.parsers[r]||(this.parsers[r]=n(r)),u=this.parsers[r],p=u.regex,d=u.map,f=t.match(p),f&&f.length){for(i={year:1900,month:0,date:1,hours:0},o=void 0,l=1,s=f.length;s>l;)c=d[l-1],c.apply&&c.apply.call(i,f[l]),l++;return a(i.year,i.month,i.date)&&(o=new Date(i.year,i.month,i.date,i.hours)),o}}}])}.call(this),function(){angular.module("rolodex.datepicker",["rolodex.dateparser","rolodex.dropdown","rolodex.position"]).constant("datepickerConfig",{formatDay:"d",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",datepickerMode:"day",minMode:"day",maxMode:"year",showWeeks:!0,startingDay:0,yearRange:20,minDate:null,maxDate:null}).controller("DatepickerController",["$scope","$attrs","$parse","$interpolate","$timeout","$log","dateFilter","datepickerConfig",function(e,t,n,r,a,o,i,l){var d,c,s;return s=this,c={$setViewValue:angular.noop},this.modes=["day","month","year"],angular.forEach(["formatDay","formatMonth","formatYear","formatDayHeader","formatDayTitle","formatMonthTitle","minMode","maxMode","showWeeks","startingDay","yearRange"],function(n,a){s[n]=angular.isDefined(t[n])?8>a?r(t[n])(e.$parent):e.$parent.$eval(t[n]):l[n]}),angular.forEach(["minDate","maxDate"],function(r){return t[r]?e.$parent.$watch(n(t[r]),function(e){return s[r]=e?new Date(e):null,s.refreshView()}):s[r]=l[r]?new Date(l[r]):null}),e.datepickerMode=e.datepickerMode||l.datepickerMode,e.uniqueId="datepicker-"+e.$id+"-"+Math.floor(1e4*Math.random()),this.activeDate=angular.isDefined(t.initDate)?e.$parent.$eval(t.initDate):new Date,this.init=function(e){return c=e,c.$render=function(){return s.render()}},this.render=function(){var e,t;return c.$modelValue&&(e=new Date(c.$modelValue),t=!isNaN(e),t?this.activeDate=e:o.error("Datepicker directive: 'ng-model' value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date."),c.$setValidity("date",t)),this.refreshView()},this.refreshView=function(){var e;return this.element?(this._refreshView(),e=c.$modelValue?new Date(c.$modelValue):null,c.$setValidity("date-disabled",!e||this.element&&!this.isDisabled(e))):void 0},this.createDateObject=function(e,t){var n;return n=c.$modelValue?new Date(c.$modelValue):null,{date:e,label:i(e,t),selected:n&&0===this.compare(e,n),disabled:this.isDisabled(e),current:0===this.compare(e,new Date)}},this.isDisabled=function(n){return this.minDate&&this.compare(n,this.minDate)<0||this.maxDate&&this.compare(n,this.maxDate)>0||t.dateDisabled&&e.dateDisabled({date:n,mode:e.datepickerMode})},this.split=function(e,t){var n;for(n=[];e.length>0;)n.push(e.splice(0,t));return n},e.select=function(t){return function(n){var r;if(!t.isDisabled(n))return e.datepickerMode===s.minMode?(r=c.$modelValue?new Date(c.$modelValue):new Date(0,0,0,0,0,0,0),r.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),c.$setViewValue(r),c.$render()):(s.activeDate=n,e.datepickerMode=s.modes[s.modes.indexOf(e.datepickerMode)-1])}}(this),e.move=function(e){var t,n;return n=s.activeDate.getFullYear()+e*(s.step.years||0),t=s.activeDate.getMonth()+e*(s.step.months||0),s.activeDate.setFullYear(n,t,1),s.refreshView()},e.toggleMode=function(t){return t=t||1,e.datepickerMode===s.maxMode&&1===t||e.datepickerMode===s.minMode&&-1===t?void 0:e.datepickerMode=s.modes[s.modes.indexOf(e.datepickerMode)+t]},e.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"},d=function(){return a(function(){return s.element[0].focus()},0,!1)},e.$on("datepicker.focus",d),e.keydown=function(t){var n;if(n=e.keys[t.which],n&&!t.shiftKey&&!t.altKey){if(t.preventDefault(),t.stopPropagation(),"enter"===n||"space"===n){if(s.isDisabled(s.activeDate))return;return e.select(s.activeDate),d()}return!t.ctrlKey||"up"!==n&&"down"!==n?(s.handleKeyDown(n,t),s.refreshView()):(e.toggleMode("up"===n?1:-1),d())}},this}]).directive("datepicker",function(){return{restrict:"EA",replace:!0,templateUrl:"rolodex_angular/template/datepicker/datepicker",scope:{datepickerMode:"=?",dateDisabled:"&"},require:["datepicker","?^ngModel"],controller:"DatepickerController",link:function(e,t,n,r){var a,o;return a=r[0],o=r[1],o?a.init(o):void 0}}}).directive("daypicker",["dateFilter",function(e){return{restrict:"EA",replace:!0,templateUrl:"rolodex_angular/template/datepicker/day",require:"^datepicker",link:function(t,n,r,a){var o,i,l,d;return l=function(e,t){return 1!==t||e%4!==0||e%100===0&&e%400!==0?o[t]:29},i=function(e,t){var n,r,a;for(r=new Array(t),n=new Date(e),a=0,n.setHours(12);t>a;)r[a++]=new Date(n),n.setDate(n.getDate()+1);return r},d=function(e){var t,n;return t=new Date(e),t.setDate(t.getDate()+4-(t.getDay()||7)),n=t.getTime(),t.setMonth(0),t.setDate(1),Math.floor(Math.round((n-t)/864e5)/7)+1},t.showWeeks=a.showWeeks,a.step={months:1},a.element=n,o=[31,28,31,30,31,30,31,31,30,31,30,31],a._refreshView=function(){var n,r,o,l,c,s,u,p,f,g,h,v;for(v=a.activeDate.getFullYear(),u=a.activeDate.getMonth(),l=new Date(v,u,1),r=a.startingDay-l.getDay(),p=r>0?7-r:-r,o=new Date(l),p>0&&o.setDate(-p+1),n=i(o,42),c=0;42>c;)n[c]=angular.extend(a.createDateObject(n[c],a.formatDay),{secondary:n[c].getMonth()!==u,uid:t.uniqueId+"-"+c}),c++;for(t.labels=new Array(7),s=0;7>s;)t.labels[s]={abbr:e(n[s].date,a.formatDayHeader).substr(0,2),full:e(n[s].date,"EEEE")},s++;if(t.title=e(a.activeDate,a.formatDayTitle),t.rows=a.split(n,7),t.showWeeks){for(t.weekNumbers=[],h=d(t.rows[0][0].date),f=t.rows.length,g=[];t.weekNumbers.push(h++)<f;);return g}},a.compare=function(e,t){return new Date(e.getFullYear(),e.getMonth(),e.getDate())-new Date(t.getFullYear(),t.getMonth(),t.getDate())},a.handleKeyDown=function(e,t){var n,r;return n=a.activeDate.getDate(),"left"===e?n-=1:"up"===e?n-=7:"right"===e?n+=1:"down"===e?n+=7:"pageup"===e||"pagedown"===e?(r=a.activeDate.getMonth()+("pageup"===e?-1:1),a.activeDate.setMonth(r,1),n=Math.min(l(a.activeDate.getFullYear(),a.activeDate.getMonth()),n)):"home"===e?n=1:"end"===e&&(n=l(a.activeDate.getFullYear(),a.activeDate.getMonth())),a.activeDate.setDate(n)},a.refreshView()}}}]).directive("monthpicker",["dateFilter",function(e){return{restrict:"EA",replace:!0,templateUrl:"rolodex_angular/template/datepicker/month",require:"^datepicker",link:function(t,n,r,a){return a.step={years:1},a.element=n,a._refreshView=function(){var n,r,o;for(r=new Array(12),o=a.activeDate.getFullYear(),n=0;12>n;)r[n]=angular.extend(a.createDateObject(new Date(o,n,1),a.formatMonth),{uid:t.uniqueId+"-"+n}),n++;return t.title=e(a.activeDate,a.formatMonthTitle),t.rows=a.split(r,3)},a.compare=function(e,t){return new Date(e.getFullYear(),e.getMonth())-new Date(t.getFullYear(),t.getMonth())},a.handleKeyDown=function(e,t){var n,r;return n=a.activeDate.getMonth(),"left"===e?n-=1:"up"===e?n-=3:"right"===e?n+=1:"down"===e?n+=3:"pageup"===e||"pagedown"===e?(r=a.activeDate.getFullYear()+("pageup"===e?-1:1),a.activeDate.setFullYear(r)):"home"===e?n=0:"end"===e&&(n=11),a.activeDate.setMonth(n)},a.refreshView()}}}]).directive("yearpicker",["dateFilter",function(e){return{restrict:"EA",replace:!0,templateUrl:"rolodex_angular/template/datepicker/year",require:"^datepicker",link:function(e,t,n,r){var a,o;return a=function(e){return parseInt((e-1)/o,10)*o+1},o=r.yearRange,r.step={years:o},r.element=t,r._refreshView=function(){var t,n,i;for(i=new Array(o),t=0,n=a(r.activeDate.getFullYear());o>t;)i[t]=angular.extend(r.createDateObject(new Date(n+t,0,1),r.formatYear),{uid:e.uniqueId+"-"+t}),t++;return e.title=[i[0].label,i[o-1].label].join(" - "),e.rows=r.split(i,5)},r.compare=function(e,t){return e.getFullYear()-t.getFullYear()},r.handleKeyDown=function(e,t){var n;return n=r.activeDate.getFullYear(),"left"===e?n-=1:"up"===e?n-=5:"right"===e?n+=1:"down"===e?n+=5:"pageup"===e||"pagedown"===e?n+=("pageup"===e?-1:1)*r.step.years:"home"===e?n=a(r.activeDate.getFullYear()):"end"===e&&(n=a(r.activeDate.getFullYear())+o-1),r.activeDate.setFullYear(n)},r.refreshView()}}}]).constant("datepickerPopupConfig",{datepickerPopup:"yyyy-MM-dd",currentText:"Today",clearText:"Clear",closeText:"Done",closeOnDateSelection:!0,appendToBody:!1,showButtonBar:!0}).directive("datepickerPopup",["$compile","$parse","$document","dateFilter","dateParser","datepickerPopupConfig","dropdownService",function(e,t,n,r,a,o,i){return{restrict:"EA",require:"ngModel",scope:{isOpen:"=?",currentText:"@",clearText:"@",closeText:"@",dateDisabled:"&"},link:function(l,d,c,s){var u,p,f,g,h,v,m,b;return f=function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})},m=function(e){var t;return e?angular.isDate(e)&&!isNaN(e)?(s.$setValidity("date",!0),e):angular.isString(e)?(t=a.parse(e,h)||new Date(e),isNaN(t)?void s.$setValidity("date",!1):(s.$setValidity("date",!0),t)):void s.$setValidity("date",!1):(s.$setValidity("date",!0),null)},h=void 0,g=angular.isDefined(c.closeOnDateSelection)?l.$parent.$eval(c.closeOnDateSelection):o.closeOnDateSelection,p=angular.isDefined(c.datepickerAppendToBody)?l.$parent.$eval(c.datepickerAppendToBody):o.appendToBody,l.showButtonBar=angular.isDefined(c.showButtonBar)?l.$parent.$eval(c.showButtonBar):o.showButtonBar,l.getText=function(e){return l[e+"Text"]||o[e+"Text"]},c.$observe("datepickerPopup",function(e){return h=e||o.datepickerPopup,s.$render()}),b=angular.element("<div datepicker-popup-wrap><div datepicker></div></div>"),b.attr({"ng-model":"date","ng-change":"dateSelection()"}),v=angular.element(b.children()[0]),c.datepickerOptions&&angular.forEach(l.$parent.$eval(c.datepickerOptions),function(e,t){return v.attr(f(t),e)}),l.watchData={},angular.forEach(["minDate","maxDate","datepickerMode"],function(e){var n,r;return c[e]&&(n=t(c[e]),l.$parent.$watch(n,function(t){return l.watchData[e]=t}),v.attr(f(e),"watchData."+e),"datepickerMode"===e)?(r=n.assign,l.$watch("watchData."+e,function(e,t){return e!==t?r(l.$parent,e):void 0})):void 0}),c.dateDisabled&&v.attr("date-disabled","dateDisabled({ date: date, mode: mode })"),s.$parsers.unshift(m),l.dateSelection=function(e){return angular.isDefined(e)&&(l.date=e),s.$setViewValue(l.date),s.$render(),g?(l.isOpen=!1,l.forceClose=!0,i.close(l),d[0].focus()):void 0},d.bind("input change keyup",function(){return l.$apply(function(){return l.date=s.$modelValue})}),s.$render=function(){var e;return e=s.$viewValue?r(s.$viewValue,h):"",d.val(e),l.date=m(s.$modelValue)},l.select=function(e){var t;return"today"===e&&(t=new Date,angular.isDate(s.$modelValue)?(e=new Date(s.$modelValue),e.setFullYear(t.getFullYear(),t.getMonth(),t.getDate())):e=new Date(t.setHours(0,0,0,0))),l.dateSelection(e)},u=e(b)(l),p?n.find("body").append(u):d.after(u),l.$on("$destroy",function(){return u.remove()})}}}]).directive("datepickerPopupWrap",function(){return{restrict:"EA",replace:!0,transclude:!0,templateUrl:"rolodex_angular/template/datepicker/popup",link:function(e,t,n){return t.bind("click",function(e){return e.preventDefault(),e.stopPropagation()})}}})}.call(this),function(){angular.module("rolodex.dropdown",[]).constant("dropdownConfig",{dropDownOpen:"data-dropdown-open"}).service("dropdownService",["$document","$timeout",function(e,t){var n,r,a;return a=null,this.open=function(t){return a||(e.bind("click",n),e.bind("keydown",r)),a&&a!==t&&(a.isOpen=!1),a=t},this.close=function(o){return a===o||o.forceClose?(o.forceClose&&(a&&(a.isOpen=!1),t(function(){return o.isOpen=!1})),a=null,e.unbind("click",n),e.unbind("keydown",r)):void 0},n=function(e){var t;return t=a.getToggleElement(),e&&(null!=t?t[0].contains(e.target):void 0)||"input"===(null!=e?e.target.nodeName.toLowerCase():void 0)?void 0:a.$apply(function(){return a.isOpen=!1})},r=function(e){return 27===e.which?(a.focusToggleElement(),n()):void 0},this}]).controller("DropdownController",["$scope","$attrs","$parse","dropdownConfig","dropdownService","$animate",function(e,t,n,r,a,o){var i,l,d,c,s,u;return c=this,d=e.$new(),i=r.dropDownOpen,l=void 0,s=angular.noop,u=t.onToggle?n(t.onToggle):angular.noop,this.init=function(r){return c.$element=r,t.isOpen?(l=n(t.isOpen),s=l.assign,e.$watch(l,function(e){return d.isOpen=!!e})):void 0},this.toggle=function(e){return d.isOpen=arguments.length?!!e:!d.isOpen},this.isOpen=function(){return d.isOpen},d.getToggleElement=function(){return c.toggleElement},d.focusToggleElement=function(){c.toggleElement&&c.toggleElement[0].focus()},d.$watch("isOpen",function(t,n){var r;return r=c.$element.find("a"),t?(c.$element.attr(i,""),d.focusToggleElement(),a.open(d),r.length&&(d.forceClose=!0,_.each(r,function(e){return angular.element(e).on("click",function(e){return a.close(d),d.$digest()})}))):(c.$element.removeAttr(i),a.close(d)),s(e,t),angular.isDefined(t)&&t!==n?u(e,{open:!!t}):void 0}),e.$on("$locationChangeSuccess",function(){return d.isOpen=!1}),e.$on("$destroy",function(){return d.$destroy()}),this}]).directive("dropdown",function(){return{controller:"DropdownController",link:function(e,t,n,r){return r.init(t)}}}).directive("dropdownToggle",function(){return{require:"?^dropdown",link:function(e,t,n,r){var a;if(r)return r.toggleElement=t,a=function(a){return a.preventDefault(),t.hasClass("disabled")||n.disabled?void 0:e.$apply(function(){return r.toggle()})},t.bind("click",a),t.attr({"aria-haspopup":!0,"aria-expanded":!1}),e.$watch(r.isOpen,function(e){return t.attr("aria-expanded",!!e)}),e.$on("$destroy",function(){return t.unbind("click",a)})}}})}.call(this),function(){angular.module("rolodex.modal",["rolodex.transition","templates"]).factory("$$stackedMap",function(){return{createNew:function(){var e;return e=[],{add:function(t,n){return e.push({key:t,value:n})},get:function(t){var n;for(n=0;n<e.length;){if(t===e[n].key)return e[n];n++}},keys:function(){var t,n;for(n=[],t=0;t<e.length;)n.push(e[t].key),t++;return n},top:function(){return e[e.length-1]},remove:function(t){var n,r;for(r=-1,n=0;n<e.length;){if(t===e[n].key){r=n;break}n++}return e.splice(r,1)[0]},removeTop:function(){return e.splice(e.length-1,1)[0]},length:function(){return e.length}}}}}).directive("modalWindow",["$modalStack","$timeout",function(e,t){return{restrict:"EA",scope:{index:"@",animate:"="},replace:!0,transclude:!0,templateUrl:function(e,t){return t.templateUrl||"rolodex_angular/template/modal/window"},link:function(n,r,a){return r.addClass(a.windowClass||""),n.size=a.size,t(function(){n.animate=!0,r[0].querySelectorAll("[autofocus]").length||r[0].focus()}),n.close=function(t){var n;return n=e.getTop(),n&&n.value.backdrop&&"static"!==n.value.backdrop&&t.target===t.currentTarget?(t.preventDefault(),t.stopPropagation(),e.dismiss(n.key,"backdrop click")):void 0}}}}]).directive("modalTransclude",function(){return{link:function(e,t,n,r,a){return a(e.$parent,function(e){return t.empty(),t.append(e)})}}}).factory("$modalStack",["$transition","$timeout","$document","$compile","$rootScope","$$stackedMap",function(e,t,n,r,a,o){var i,l,d,c,s,u,p,f,g;return c=function(){var e,t,n;for(n=-1,t=p.keys(),e=0;e<t.length;)p.get(t[e]).value.backdrop&&(n=e),e++;return n},g=function(e){var t,r;t=n.find("body").eq(0),r=p.get(e).value,p.remove(e),f(r.modalDomEl,r.modalScope,300,function(){r.modalScope.$destroy(),t.toggleClass(l,p.length()>0),u()})},u=function(){var e,t,n;e&&-1===c()&&(n=t,f(e,t,150,function(){n.$destroy(),n=null}),e=void 0,t=void 0)},f=function(n,r,a,o){var i,l,d;i=function(){i.done||(i.done=!0,n.remove(),o&&o())},r.animate=!1,d=e.transitionEndEventName,d?(l=t(i,a),n.bind(d,function(){t.cancel(l),i(),r.$apply()})):t(i)},l="modal-open",d=void 0,s=void 0,p=o.createNew(),i={},a.$watch(c,function(e){s&&(s.index=e)}),n.bind("keydown",function(e){var t;t=void 0,27===e.which&&(t=p.top(),t&&t.value.keyboard&&(e.preventDefault(),a.$apply(function(){i.dismiss(t.key,"escape key press")})))}),i.open=function(e,t){var o,i,u,f,g;p.add(e,{deferred:t.deferred,modalScope:t.scope,backdrop:t.backdrop,keyboard:t.keyboard}),u=n.find("body").eq(0),f=c(),f>=0&&!d&&(s=a.$new(!0),s.index=f,o=angular.element("<div modal-backdrop></div>"),o.attr("backdrop-class",t.backdropClass),d=r(o)(s),u.append(d)),i=angular.element("<div modal-window></div>"),i.attr({"template-url":t.windowTemplateUrl,"window-class":t.windowClass,size:t.size,index:p.length()-1,animate:"animate"}).html(t.content),g=r(i)(t.scope),p.top().value.modalDomEl=g,u.append(g),u.addClass(l)},i.close=function(e,t){var n;n=p.get(e),n&&(n.value.deferred.resolve(t),g(e))},i.dismiss=function(e,t){var n;n=p.get(e),n&&(n.value.deferred.reject(t),g(e))},i.dismissAll=function(e){var t;for(t=this.getTop();t;)this.dismiss(t.key,e),t=this.getTop()},i.getTop=function(){return p.top()},i}]).provider("$modal",function(){var e;return e={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(t,n,r,a,o,i,l){var d,c,s;return s=function(e){return e.template?r.when(e.template):a.get(angular.isFunction(e.templateUrl)?e.templateUrl():e.templateUrl,{cache:o}).then(function(e){return e.data})},c=function(e){var n;return n=[],angular.forEach(e,function(e){(angular.isFunction(e)||angular.isArray(e))&&n.push(r.when(t.invoke(e)))}),n},d={},d.open=function(t){var a,o,d,u,p,f;if(d=r.defer(),o=r.defer(),a={result:d.promise,opened:o.promise,close:function(e){l.close(a,e)},dismiss:function(e){l.dismiss(a,e)}},t=angular.extend({},e.options,t),t.resolve=t.resolve||{},!t.template&&!t.templateUrl)throw new Error("One of template or templateUrl options is required.");return f=r.all([s(t)].concat(c(t.resolve))),f.then(p=function(e){var r,o,c,s;return c=(t.scope||n).$new(),c.$close=a.close,c.$dismiss=a.dismiss,r=void 0,o={},s=1,t.controller&&(o.$scope=c,o.$modalInstance=a,angular.forEach(t.resolve,function(t,n){o[n]=e[s++]}),r=i(t.controller,o),t.controller&&(c[t.controllerAs]=r)),l.open(a,{scope:c,deferred:d,content:e[0],backdrop:t.backdrop,keyboard:t.keyboard,backdropClass:t.backdropClass,windowClass:t.windowClass,windowTemplateUrl:t.windowTemplateUrl,size:t.size})},u=function(e){return d.reject(e)}),f.then(function(){return o.resolve(!0)},function(){return o.reject(!1)}),a},d}]}})}.call(this),function(){angular.module("rolodex.position",[]).factory("$position",["$document","$window",function(e,t){var n,r,a;return n=function(e,n){return e.currentStyle?e.currentStyle[n]:t.getComputedStyle?t.getComputedStyle(e)[n]:e.style[n]},r=function(e){return"static"===(n(e,"position")||"static")},a=function(t){var n,a;for(n=e[0],a=t.offsetParent||n;a&&a!==n&&r(a);)a=a.offsetParent;return a||n},{position:function(t){var n,r,o,i;return r=this.offset(t),o={top:0,left:0},i=a(t[0]),i!==e[0]&&(o=this.offset(angular.element(i)),o.top+=i.clientTop-i.scrollTop,o.left+=i.clientLeft-i.scrollLeft),n=t[0].getBoundingClientRect(),{width:n.width||t.prop("offsetWidth"),height:n.height||t.prop("offsetHeight"),top:r.top-o.top,left:r.left-o.left}},offset:function(n){var r;return r=n[0].getBoundingClientRect(),{width:r.width||n.prop("offsetWidth"),height:r.height||n.prop("offsetHeight"),top:r.top+(t.pageYOffset||e[0].documentElement.scrollTop),left:r.left+(t.pageXOffset||e[0].documentElement.scrollLeft)}},positionElements:function(e,t,n,r){var a,o,i,l,d,c,s,u,p;switch(l=n.split("-"),o=l[0],i=l[1]||"center",a=void 0,p=void 0,s=void 0,u=void 0,a=r?this.offset(e):this.position(e),p=t.prop("offsetWidth"),s=t.prop("offsetHeight"),c={center:function(){return a.left+a.width/2-p/2},left:function(){return a.left},right:function(){return a.left+a.width}},d={center:function(){return a.top+a.height/2-s/2},top:function(){return a.top},bottom:function(){return a.top+a.height}},o){case"right":u={top:d[i](),left:c[o]()};break;case"left":u={top:d[i](),left:a.left-p};break;case"bottom":u={top:d[o](),left:c[i]()};break;default:u={top:a.top-s,left:c[i]()}}return u}}}])}.call(this),function(){angular.module("rolodex.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(e,t,n){var r,a,o,i,l;return o=function(e){var t;for(t in e)if(void 0!==i.style[t])return e[t]},r=function(a,o,i){var l,d,c;return i=i||{},l=e.defer(),d=r[i.animation?"animationEndEventName":"transitionEndEventName"],c=function(e){n.$apply(function(){a.unbind(d,c),l.resolve(a)})},d&&a.bind(d,c),t(function(){angular.isString(o)?a.addClass(o):angular.isFunction(o)?o(a):angular.isObject(o)&&a.css(o),d||l.resolve(a)}),l.promise.cancel=function(){d&&a.unbind(d,c),l.reject("Transition cancelled")},l.promise},i=document.createElement("trans"),l={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},a={WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"},r.transitionEndEventName=o(l),r.animationEndEventName=o(a),r}])}.call(this);
data/gulpfile.js ADDED
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ var gulp = require('gulp');
4
+ var path = require('path');
5
+ var del = require('del');
6
+ var source = require('vinyl-source-stream');
7
+ var $ = require('gulp-load-plugins')();
8
+
9
+ var assetPath = 'vendor/assets/javascripts/rolodex_angular';
10
+ var distPath = 'dist';
11
+ var tmpPath = '.tmp';
12
+
13
+ // Compiles coffee to js, annotates Angular dependencies
14
+ gulp.task('js', ['js-template-cache'], function() {
15
+ return gulp.src(path.join(assetPath, '/**/*.coffee'))
16
+ .pipe($.coffee())
17
+ .pipe($.ngAnnotate())
18
+ .pipe(gulp.dest(tmpPath))
19
+ });
20
+
21
+ // A hack for non-Ruby projects to fake the templates namespace (which lives in a gem)
22
+ gulp.task('js-template-cache', function() {
23
+ var stream = source('templates.js');
24
+ stream.end("(function() { angular.module('templates', []); }).call(this);");
25
+ stream.pipe(gulp.dest(tmpPath));
26
+ });
27
+
28
+ // Compiles haml to js for Angular templateCache
29
+ gulp.task('templates', function() {
30
+ return gulp.src(path.join(assetPath, '/template/**/*.ngt'))
31
+ .pipe($.minifyHtml({ empty: true, spare: true, quotes: true }))
32
+ .pipe($.angularTemplatecache('templates.js', {
33
+ module: 'rolodex',
34
+ root: 'rolodex_angular/template',
35
+ transformUrl: function(url) {
36
+ return url.replace(/\.ngt$/, '')
37
+ }
38
+ }))
39
+ .pipe(gulp.dest(tmpPath));
40
+ });
41
+
42
+ // Builds the dist files
43
+ gulp.task('build', ['js', 'templates'], function() {
44
+ return gulp.src(path.join(tmpPath, '/**/*.js'))
45
+ .pipe($.concat('rolodex.js'))
46
+ .pipe(gulp.dest(distPath))
47
+ .pipe($.uglify())
48
+ .pipe($.rename({extname: '.min.js'}))
49
+ .pipe(gulp.dest(distPath))
50
+ });
51
+
52
+ // Cleans the build folder and tmp folder for development
53
+ gulp.task('clean', function (done) {
54
+ del([tmpPath, distPath], done);
55
+ });
56
+
57
+ gulp.task('default', ['clean', 'build']);
@@ -1,3 +1,3 @@
1
1
  module Rolodex
2
- VERSION = "2.0.2"
2
+ VERSION = "2.0.4"
3
3
  end
data/package.json CHANGED
@@ -1,10 +1,29 @@
1
1
  {
2
2
  "name": "rolodex",
3
- "version": "2.0.2",
4
- "description": "Style for Belly",
5
- "main": "",
3
+ "author": "Belly",
4
+ "keywords": [
5
+ "rolodex"
6
+ ],
7
+ "license": "ISC",
8
+ "bugs": {
9
+ "url": "https://github.com/bellycard/rolodex/issues"
10
+ },
11
+ "homepage": "https://github.com/bellycard/rolodex",
6
12
  "dependencies": {},
7
13
  "devDependencies": {
14
+ "del": "^2.0.2",
15
+ "gulp": "^3.9.0",
16
+ "gulp-angular-templatecache": "^1.7.0",
17
+ "gulp-coffee": "^2.3.1",
18
+ "gulp-concat": "^2.6.0",
19
+ "gulp-haml": "^0.1.5",
20
+ "gulp-load-plugins": "^1.0.0-rc.1",
21
+ "gulp-minify-html": "^1.0.4",
22
+ "gulp-ng-annotate": "^1.1.0",
23
+ "gulp-rename": "^1.2.2",
24
+ "gulp-ruby-haml": "0.0.4",
25
+ "gulp-sourcemaps": "^1.6.0",
26
+ "gulp-uglify": "^1.4.1",
8
27
  "karma": "^0.12.19",
9
28
  "karma-chrome-launcher": "^0.1.4",
10
29
  "karma-coffee-preprocessor": "^0.2.1",
@@ -12,7 +31,8 @@
12
31
  "karma-firefox-launcher": "^0.1.3",
13
32
  "karma-jasmine": "^0.1.5",
14
33
  "karma-ng-html2js-preprocessor": "^0.1.0",
15
- "karma-safari-launcher": "^0.1.1"
34
+ "karma-safari-launcher": "^0.1.1",
35
+ "vinyl-source-stream": "^1.1.0"
16
36
  },
17
37
  "scripts": {
18
38
  "test_angular": "./node_modules/karma/bin/karma start"
@@ -20,14 +40,5 @@
20
40
  "repository": {
21
41
  "type": "git",
22
42
  "url": "https://github.com/bellycard/rolodex.git"
23
- },
24
- "keywords": [
25
- "rolodex"
26
- ],
27
- "author": "Belly",
28
- "license": "ISC",
29
- "bugs": {
30
- "url": "https://github.com/bellycard/rolodex/issues"
31
- },
32
- "homepage": "https://github.com/bellycard/rolodex"
43
+ }
33
44
  }
@@ -4,7 +4,8 @@ angular.module('rolodex.dropdown', [])
4
4
 
5
5
  .service('dropdownService', [
6
6
  '$document'
7
- ($document) ->
7
+ '$timeout'
8
+ ($document, $timeout) ->
8
9
  openScope = null
9
10
 
10
11
  @open = (dropdownScope) ->
@@ -21,6 +22,9 @@ angular.module('rolodex.dropdown', [])
21
22
  if dropdownScope.forceClose
22
23
  openScope.isOpen = false if openScope
23
24
 
25
+ $timeout ->
26
+ dropdownScope.isOpen = false
27
+
24
28
  openScope = null
25
29
  $document.unbind 'click', closeDropdown
26
30
  $document.unbind 'keydown', escapeKeyBind
@@ -1,4 +1,3 @@
1
-
2
1
  ###*
3
2
  A set of utility methods that can be use to retrieve position of DOM elements.
4
3
  It is meant to be used where we need to absolute-position DOM elements in
@@ -0,0 +1,12 @@
1
+ <div class="panel panel-default">
2
+ <div class="panel-heading">
3
+ <h4 class="panel-title">
4
+ <a accordion-transclude="heading" class="accordion-toggle" ng-click="toggleOpen()">
5
+ <span ng-class="{'text-muted': isDisabled}">{{heading}}</span>
6
+ </a>
7
+ </h4>
8
+ </div>
9
+ <div class="panel-collapse" collapse="!isOpen">
10
+ <div class="panel-body" ng-transclude></div>
11
+ </div>
12
+ </div>
@@ -0,0 +1 @@
1
+ <div class="panel-group" ng-transclude></div>
@@ -0,0 +1,4 @@
1
+ <div ng-class="['alert-banner-' + (type || 'warning'), closeable ? 'alert-dismissable' : null]" role="alert">
2
+ <button aria-hidden="true" class="i-close" ng-click="close()" ng-show="closeable" type="button">&times;</button>
3
+ <div ng-transclude></div>
4
+ </div>
@@ -0,0 +1,5 @@
1
+ <div ng-keydown="keydown($event)" ng-switch="datepickerMode" role="application">
2
+ <daypicker ng-switch-when="day" tabindex="0"></daypicker>
3
+ <monthpicker ng-switch-when="month" tabindex="0"></monthpicker>
4
+ <yearpicker ng-switch-when="year" tabindex="0"></yearpicker>
5
+ </div>
@@ -0,0 +1,27 @@
1
+ <table aria-activedescendant="{{ activeDateId }}" aria-labelledby="{{uniqueId }}-title" role="grid">
2
+ <thead>
3
+ <tr>
4
+ <th class="prev available" ng-click="move(-1)" tabindex="-1">
5
+ <div class="i-caret"></div>
6
+ </th>
7
+ <th class="month" colspan="{{ 5 + showWeeks }}">
8
+ {{ title }}
9
+ </th>
10
+ <th class="next available" ng-click="move(1)" tabindex="-1">
11
+ <div class="i-caret"></div>
12
+ </th>
13
+ </tr>
14
+ <tr>
15
+ <th class="text-center" ng-repeat="label in labels track by $index">
16
+ <small aria-label="{{ label.full}}">{{ label.abbr }}</small>
17
+ </th>
18
+ </tr>
19
+ </thead>
20
+ <tbody>
21
+ <tr ng-repeat="row in rows track by $index">
22
+ <td aria-disabled="{{ !!dt.disabled }}" id="{{ dt.uid }}" ng-class="{active: dt.selected, available: !dt.disabled, off: dt.disabled}" ng-click="select(dt.date, dt.disabled)" ng-disabled="dt.disabled" ng-repeat="dt in row track by dt.date" role="gridcell" tabindex="-1">
23
+ <span ng-class="{'text-muted': dt.secondary}">{{ dt.label }}</span>
24
+ </td>
25
+ </tr>
26
+ </tbody>
27
+ </table>
@@ -0,0 +1,30 @@
1
+ <table aria-activedescendant="{{ activeDateId }}" aria-labelledby="{{ uniqueId }}-title" role="grid">
2
+ <thead>
3
+ <tr>
4
+ <th>
5
+ <button class="btn btn-boring pull-left" ng-click="move(-1)" tabindex="-1" type="button">
6
+ <i class="i-caret"></i>
7
+ </button>
8
+ </th>
9
+ <th>
10
+ <button aria-atomic="true" aria-live="assertive" class="btn btn-boring" id="{{ uniqueId }}-title" ng-click="toggleMode()" role="heading" style="width:100%;" tabindex="-1" type="button">
11
+ <strong>{{ title }}</strong>
12
+ </button>
13
+ </th>
14
+ <th>
15
+ <button class="btn btn-boring pull-right" ng-click="move(1)" tabindex="-1" type="button">
16
+ <i class="i-caret"></i>
17
+ </button>
18
+ </th>
19
+ </tr>
20
+ </thead>
21
+ <tbody>
22
+ <tr ng-repeat="row in rows track by $index">
23
+ <td aria-disabled="{{ !!dt.disabled }}" class="text-center" id="{{ dt.uid }}" ng-repeat="dt in row track by dt.date" role="gridcell">
24
+ <button class="btn btn-boring" ng-class="{'btn-info': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" style="width:100%;" tabindex="-1" type="button">
25
+ <span ng-class="{'text-info': dt.current}">{{ dt.label }}</span>
26
+ </button>
27
+ </td>
28
+ </tr>
29
+ </tbody>
30
+ </table>
@@ -0,0 +1,3 @@
1
+ <div class="date-picker-solo daterangepicker dropdown-menu group" data-dropdown-content ng-keydown="keydown($event)">
2
+ <div ng-transclude></div>
3
+ </div>
@@ -0,0 +1,30 @@
1
+ <table aria-activedescendant="{{ activeDateId }}" aria-labelledby="{{ uniqueId }}-title" role="grid">
2
+ <thead>
3
+ <tr>
4
+ <th>
5
+ <button class="btn btn-boring pull-left" ng-click="move(-1)" tabindex="-1" type="button">
6
+ <i class="i-caret"></i>
7
+ </button>
8
+ </th>
9
+ <th colspan="3">
10
+ <button aria-atomic="true" aria-live="assertive" class="btn btn-boring" id="{{ uniqueId }}-title" ng-click="toggleMode()" role="heading" style="width:100%;" tabindex="-1" type="button">
11
+ <strong>{{ title }}</strong>
12
+ </button>
13
+ </th>
14
+ <th>
15
+ <button class="btn btn-boring pull-right" ng-click="move(1)" tabindex="-1" type="button">
16
+ <i class="i-caret"></i>
17
+ </button>
18
+ </th>
19
+ </tr>
20
+ </thead>
21
+ <tbody>
22
+ <tr ng-repeat="row in rows track by $index">
23
+ <td aria-disabled="{{ !!dt.disabled }}" class="text-center" id="{{ dt.uid }}" ng-repeat="dt in row track by dt.date" role="gridcell">
24
+ <button class="btn btn-boring" ng-class="{'btn-info': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" style="width:100%;" tabindex="-1" type="button">
25
+ <span ng-class="{'text-info': dt.current}">{{ dt.label }}</span>
26
+ </button>
27
+ </td>
28
+ </tr>
29
+ </tbody>
30
+ </table>
@@ -0,0 +1,3 @@
1
+ <div class="modal" ng-class="{'modal-fade': animate}" ng-click="close($event)" role="dialog" tabindex="-1">
2
+ <div class="modal-content" modal-transclude></div>
3
+ </div>
@@ -307,6 +307,7 @@ textarea
307
307
  +rem(padding-right, 44px)
308
308
 
309
309
  .form-validating
310
+ background-image: url("#{$image-path}/forms/icon-validating.gif")
310
311
  background-image: image-url("forms/icon-validating.gif")
311
312
 
312
313
  .form-valid
@@ -3,8 +3,10 @@
3
3
 
4
4
  =background-svg($file-name, $size...)
5
5
 
6
+ background-image: url("#{$image-path}/#{$file-name}.png")
7
+ background-image: url("#{$image-path}/#{$file-name}.svg")
6
8
  background-image: image-url("#{$file-name}.png")
7
9
  background-image: image-url("#{$file-name}.svg")
8
10
 
9
11
  @if length($size) > 0
10
- +rem(background-size, $size)
12
+ +rem(background-size, $size...)
@@ -33,12 +33,10 @@
33
33
  +rem(font-size, $s-foxtrot)
34
34
 
35
35
  =s-golf
36
- +rem(font, $s-golf $font-family-helvetica)
37
- +rem(line-height, $baseline-small)
36
+ +rem(font, #{$s-golf}/#{$baseline-small} $font-family-helvetica)
38
37
 
39
38
  =s-hotel
40
- +rem(font, $s-hotel $font-family-helvetica)
41
- +rem(line-height, $baseline-small)
39
+ +rem(font, #{$s-hotel}/#{$baseline-small} $font-family-helvetica)
42
40
 
43
41
  // Headings
44
42
 
@@ -10,3 +10,7 @@ $transition-all: all $transition-duration $transition-function;
10
10
  // Icons
11
11
 
12
12
  $i-transparent: .65;
13
+
14
+ // Image Path
15
+
16
+ $image-path: '/images';
@@ -19,7 +19,7 @@ $s-hotel: 11px;
19
19
 
20
20
  // Font families
21
21
 
22
- $font-family-open-sans: "Open Sans", Verdana, Helvetica, sans-serif;
22
+ $font-family-open-sans: "Open Sans", Helvetica, sans-serif;
23
23
  $font-family-helvetica: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
24
24
  $font-family-monospace: "Source Code Pro", Inconsolata, "Lucida Console", Terminal, "Courier New", Courier;
25
25
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rolodex
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - AJ Self, Darby Frey, Shay Howe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-22 00:00:00.000000000 Z
11
+ date: 2015-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: angular-html2js
@@ -107,6 +107,9 @@ files:
107
107
  - README.md
108
108
  - Rakefile
109
109
  - bower.json
110
+ - dist/rolodex.js
111
+ - dist/rolodex.min.js
112
+ - gulpfile.js
110
113
  - karma.conf.js
111
114
  - lib/rolodex.rb
112
115
  - lib/rolodex/engine.rb
@@ -114,12 +117,6 @@ files:
114
117
  - lib/rolodex/version.rb
115
118
  - package.json
116
119
  - rolodex.gemspec
117
- - spec/javascripts/rolodex_angular/modal.spec.coffee
118
- - spec/spec_helper.rb
119
- - spec/test_lib/angular-mocks.js
120
- - spec/test_lib/angular.js
121
- - spec/test_lib/jquery-2.1.1.js
122
- - spec/test_lib/lodash.js
123
120
  - vendor/assets/.keep
124
121
  - vendor/assets/images/alerts/icon-danger-dark.png
125
122
  - vendor/assets/images/alerts/icon-danger-dark.svg
@@ -188,15 +185,15 @@ files:
188
185
  - vendor/assets/javascripts/rolodex_angular/src/modal.coffee
189
186
  - vendor/assets/javascripts/rolodex_angular/src/position.coffee
190
187
  - vendor/assets/javascripts/rolodex_angular/src/transition.coffee
191
- - vendor/assets/javascripts/rolodex_angular/template/accordion/accordion-group.ngt.haml
192
- - vendor/assets/javascripts/rolodex_angular/template/accordion/accordion.ngt.haml
193
- - vendor/assets/javascripts/rolodex_angular/template/alert/alert.ngt.haml
194
- - vendor/assets/javascripts/rolodex_angular/template/datepicker/datepicker.ngt.haml
195
- - vendor/assets/javascripts/rolodex_angular/template/datepicker/day.ngt.haml
196
- - vendor/assets/javascripts/rolodex_angular/template/datepicker/month.ngt.haml
197
- - vendor/assets/javascripts/rolodex_angular/template/datepicker/popup.ngt.haml
198
- - vendor/assets/javascripts/rolodex_angular/template/datepicker/year.ngt.haml
199
- - vendor/assets/javascripts/rolodex_angular/template/modal/window.ngt.haml
188
+ - vendor/assets/javascripts/rolodex_angular/template/accordion/accordion-group.ngt
189
+ - vendor/assets/javascripts/rolodex_angular/template/accordion/accordion.ngt
190
+ - vendor/assets/javascripts/rolodex_angular/template/alert/alert.ngt
191
+ - vendor/assets/javascripts/rolodex_angular/template/datepicker/datepicker.ngt
192
+ - vendor/assets/javascripts/rolodex_angular/template/datepicker/day.ngt
193
+ - vendor/assets/javascripts/rolodex_angular/template/datepicker/month.ngt
194
+ - vendor/assets/javascripts/rolodex_angular/template/datepicker/popup.ngt
195
+ - vendor/assets/javascripts/rolodex_angular/template/datepicker/year.ngt
196
+ - vendor/assets/javascripts/rolodex_angular/template/modal/window.ngt
200
197
  - vendor/assets/javascripts/vendor/jquery-1.10.2.min.js
201
198
  - vendor/assets/javascripts/vendor/modernizr-2.6.2.min.js
202
199
  - vendor/assets/stylesheets/rolodex.sass
@@ -251,14 +248,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
251
248
  version: '0'
252
249
  requirements: []
253
250
  rubyforge_project:
254
- rubygems_version: 2.4.6
251
+ rubygems_version: 2.0.3
255
252
  signing_key:
256
253
  specification_version: 4
257
254
  summary: Rolodex
258
- test_files:
259
- - spec/javascripts/rolodex_angular/modal.spec.coffee
260
- - spec/spec_helper.rb
261
- - spec/test_lib/angular-mocks.js
262
- - spec/test_lib/angular.js
263
- - spec/test_lib/jquery-2.1.1.js
264
- - spec/test_lib/lodash.js
255
+ test_files: []
@@ -1,13 +0,0 @@
1
- describe 'roModal', ->
2
-
3
- beforeEach module('rolodex.modal')
4
-
5
- xdescribe 'basic modal with defaults', ->
6
- xdescribe 'configuring options via provider', ->
7
- xdescribe 'controller', ->
8
- xdescribe 'resolve', ->
9
- xdescribe 'scope', ->
10
- xdescribe 'keyboard interaction', ->
11
- xdescribe 'backdrop', ->
12
- xdescribe 'window', ->
13
- xdescribe 'sizing', ->
data/spec/spec_helper.rb DELETED
File without changes
@@ -1,2173 +0,0 @@
1
- /**
2
- * @license AngularJS v1.2.21
3
- * (c) 2010-2014 Google, Inc. http://angularjs.org
4
- * License: MIT
5
- */
6
- (function(window, angular, undefined) {
7
-
8
- 'use strict';
9
-
10
- /**
11
- * @ngdoc object
12
- * @name angular.mock
13
- * @description
14
- *
15
- * Namespace from 'angular-mocks.js' which contains testing related code.
16
- */
17
- angular.mock = {};
18
-
19
- /**
20
- * ! This is a private undocumented service !
21
- *
22
- * @name $browser
23
- *
24
- * @description
25
- * This service is a mock implementation of {@link ng.$browser}. It provides fake
26
- * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr,
27
- * cookies, etc...
28
- *
29
- * The api of this service is the same as that of the real {@link ng.$browser $browser}, except
30
- * that there are several helper methods available which can be used in tests.
31
- */
32
- angular.mock.$BrowserProvider = function() {
33
- this.$get = function() {
34
- return new angular.mock.$Browser();
35
- };
36
- };
37
-
38
- angular.mock.$Browser = function() {
39
- var self = this;
40
-
41
- this.isMock = true;
42
- self.$$url = "http://server/";
43
- self.$$lastUrl = self.$$url; // used by url polling fn
44
- self.pollFns = [];
45
-
46
- // TODO(vojta): remove this temporary api
47
- self.$$completeOutstandingRequest = angular.noop;
48
- self.$$incOutstandingRequestCount = angular.noop;
49
-
50
-
51
- // register url polling fn
52
-
53
- self.onUrlChange = function(listener) {
54
- self.pollFns.push(
55
- function() {
56
- if (self.$$lastUrl != self.$$url) {
57
- self.$$lastUrl = self.$$url;
58
- listener(self.$$url);
59
- }
60
- }
61
- );
62
-
63
- return listener;
64
- };
65
-
66
- self.cookieHash = {};
67
- self.lastCookieHash = {};
68
- self.deferredFns = [];
69
- self.deferredNextId = 0;
70
-
71
- self.defer = function(fn, delay) {
72
- delay = delay || 0;
73
- self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
74
- self.deferredFns.sort(function(a,b){ return a.time - b.time;});
75
- return self.deferredNextId++;
76
- };
77
-
78
-
79
- /**
80
- * @name $browser#defer.now
81
- *
82
- * @description
83
- * Current milliseconds mock time.
84
- */
85
- self.defer.now = 0;
86
-
87
-
88
- self.defer.cancel = function(deferId) {
89
- var fnIndex;
90
-
91
- angular.forEach(self.deferredFns, function(fn, index) {
92
- if (fn.id === deferId) fnIndex = index;
93
- });
94
-
95
- if (fnIndex !== undefined) {
96
- self.deferredFns.splice(fnIndex, 1);
97
- return true;
98
- }
99
-
100
- return false;
101
- };
102
-
103
-
104
- /**
105
- * @name $browser#defer.flush
106
- *
107
- * @description
108
- * Flushes all pending requests and executes the defer callbacks.
109
- *
110
- * @param {number=} number of milliseconds to flush. See {@link #defer.now}
111
- */
112
- self.defer.flush = function(delay) {
113
- if (angular.isDefined(delay)) {
114
- self.defer.now += delay;
115
- } else {
116
- if (self.deferredFns.length) {
117
- self.defer.now = self.deferredFns[self.deferredFns.length-1].time;
118
- } else {
119
- throw new Error('No deferred tasks to be flushed');
120
- }
121
- }
122
-
123
- while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
124
- self.deferredFns.shift().fn();
125
- }
126
- };
127
-
128
- self.$$baseHref = '';
129
- self.baseHref = function() {
130
- return this.$$baseHref;
131
- };
132
- };
133
- angular.mock.$Browser.prototype = {
134
-
135
- /**
136
- * @name $browser#poll
137
- *
138
- * @description
139
- * run all fns in pollFns
140
- */
141
- poll: function poll() {
142
- angular.forEach(this.pollFns, function(pollFn){
143
- pollFn();
144
- });
145
- },
146
-
147
- addPollFn: function(pollFn) {
148
- this.pollFns.push(pollFn);
149
- return pollFn;
150
- },
151
-
152
- url: function(url, replace) {
153
- if (url) {
154
- this.$$url = url;
155
- return this;
156
- }
157
-
158
- return this.$$url;
159
- },
160
-
161
- cookies: function(name, value) {
162
- if (name) {
163
- if (angular.isUndefined(value)) {
164
- delete this.cookieHash[name];
165
- } else {
166
- if (angular.isString(value) && //strings only
167
- value.length <= 4096) { //strict cookie storage limits
168
- this.cookieHash[name] = value;
169
- }
170
- }
171
- } else {
172
- if (!angular.equals(this.cookieHash, this.lastCookieHash)) {
173
- this.lastCookieHash = angular.copy(this.cookieHash);
174
- this.cookieHash = angular.copy(this.cookieHash);
175
- }
176
- return this.cookieHash;
177
- }
178
- },
179
-
180
- notifyWhenNoOutstandingRequests: function(fn) {
181
- fn();
182
- }
183
- };
184
-
185
-
186
- /**
187
- * @ngdoc provider
188
- * @name $exceptionHandlerProvider
189
- *
190
- * @description
191
- * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors
192
- * passed into the `$exceptionHandler`.
193
- */
194
-
195
- /**
196
- * @ngdoc service
197
- * @name $exceptionHandler
198
- *
199
- * @description
200
- * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed
201
- * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration
202
- * information.
203
- *
204
- *
205
- * ```js
206
- * describe('$exceptionHandlerProvider', function() {
207
- *
208
- * it('should capture log messages and exceptions', function() {
209
- *
210
- * module(function($exceptionHandlerProvider) {
211
- * $exceptionHandlerProvider.mode('log');
212
- * });
213
- *
214
- * inject(function($log, $exceptionHandler, $timeout) {
215
- * $timeout(function() { $log.log(1); });
216
- * $timeout(function() { $log.log(2); throw 'banana peel'; });
217
- * $timeout(function() { $log.log(3); });
218
- * expect($exceptionHandler.errors).toEqual([]);
219
- * expect($log.assertEmpty());
220
- * $timeout.flush();
221
- * expect($exceptionHandler.errors).toEqual(['banana peel']);
222
- * expect($log.log.logs).toEqual([[1], [2], [3]]);
223
- * });
224
- * });
225
- * });
226
- * ```
227
- */
228
-
229
- angular.mock.$ExceptionHandlerProvider = function() {
230
- var handler;
231
-
232
- /**
233
- * @ngdoc method
234
- * @name $exceptionHandlerProvider#mode
235
- *
236
- * @description
237
- * Sets the logging mode.
238
- *
239
- * @param {string} mode Mode of operation, defaults to `rethrow`.
240
- *
241
- * - `rethrow`: If any errors are passed into the handler in tests, it typically
242
- * means that there is a bug in the application or test, so this mock will
243
- * make these tests fail.
244
- * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log`
245
- * mode stores an array of errors in `$exceptionHandler.errors`, to allow later
246
- * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and
247
- * {@link ngMock.$log#reset reset()}
248
- */
249
- this.mode = function(mode) {
250
- switch(mode) {
251
- case 'rethrow':
252
- handler = function(e) {
253
- throw e;
254
- };
255
- break;
256
- case 'log':
257
- var errors = [];
258
-
259
- handler = function(e) {
260
- if (arguments.length == 1) {
261
- errors.push(e);
262
- } else {
263
- errors.push([].slice.call(arguments, 0));
264
- }
265
- };
266
-
267
- handler.errors = errors;
268
- break;
269
- default:
270
- throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!");
271
- }
272
- };
273
-
274
- this.$get = function() {
275
- return handler;
276
- };
277
-
278
- this.mode('rethrow');
279
- };
280
-
281
-
282
- /**
283
- * @ngdoc service
284
- * @name $log
285
- *
286
- * @description
287
- * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays
288
- * (one array per logging level). These arrays are exposed as `logs` property of each of the
289
- * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
290
- *
291
- */
292
- angular.mock.$LogProvider = function() {
293
- var debug = true;
294
-
295
- function concat(array1, array2, index) {
296
- return array1.concat(Array.prototype.slice.call(array2, index));
297
- }
298
-
299
- this.debugEnabled = function(flag) {
300
- if (angular.isDefined(flag)) {
301
- debug = flag;
302
- return this;
303
- } else {
304
- return debug;
305
- }
306
- };
307
-
308
- this.$get = function () {
309
- var $log = {
310
- log: function() { $log.log.logs.push(concat([], arguments, 0)); },
311
- warn: function() { $log.warn.logs.push(concat([], arguments, 0)); },
312
- info: function() { $log.info.logs.push(concat([], arguments, 0)); },
313
- error: function() { $log.error.logs.push(concat([], arguments, 0)); },
314
- debug: function() {
315
- if (debug) {
316
- $log.debug.logs.push(concat([], arguments, 0));
317
- }
318
- }
319
- };
320
-
321
- /**
322
- * @ngdoc method
323
- * @name $log#reset
324
- *
325
- * @description
326
- * Reset all of the logging arrays to empty.
327
- */
328
- $log.reset = function () {
329
- /**
330
- * @ngdoc property
331
- * @name $log#log.logs
332
- *
333
- * @description
334
- * Array of messages logged using {@link ngMock.$log#log}.
335
- *
336
- * @example
337
- * ```js
338
- * $log.log('Some Log');
339
- * var first = $log.log.logs.unshift();
340
- * ```
341
- */
342
- $log.log.logs = [];
343
- /**
344
- * @ngdoc property
345
- * @name $log#info.logs
346
- *
347
- * @description
348
- * Array of messages logged using {@link ngMock.$log#info}.
349
- *
350
- * @example
351
- * ```js
352
- * $log.info('Some Info');
353
- * var first = $log.info.logs.unshift();
354
- * ```
355
- */
356
- $log.info.logs = [];
357
- /**
358
- * @ngdoc property
359
- * @name $log#warn.logs
360
- *
361
- * @description
362
- * Array of messages logged using {@link ngMock.$log#warn}.
363
- *
364
- * @example
365
- * ```js
366
- * $log.warn('Some Warning');
367
- * var first = $log.warn.logs.unshift();
368
- * ```
369
- */
370
- $log.warn.logs = [];
371
- /**
372
- * @ngdoc property
373
- * @name $log#error.logs
374
- *
375
- * @description
376
- * Array of messages logged using {@link ngMock.$log#error}.
377
- *
378
- * @example
379
- * ```js
380
- * $log.error('Some Error');
381
- * var first = $log.error.logs.unshift();
382
- * ```
383
- */
384
- $log.error.logs = [];
385
- /**
386
- * @ngdoc property
387
- * @name $log#debug.logs
388
- *
389
- * @description
390
- * Array of messages logged using {@link ngMock.$log#debug}.
391
- *
392
- * @example
393
- * ```js
394
- * $log.debug('Some Error');
395
- * var first = $log.debug.logs.unshift();
396
- * ```
397
- */
398
- $log.debug.logs = [];
399
- };
400
-
401
- /**
402
- * @ngdoc method
403
- * @name $log#assertEmpty
404
- *
405
- * @description
406
- * Assert that the all of the logging methods have no logged messages. If messages present, an
407
- * exception is thrown.
408
- */
409
- $log.assertEmpty = function() {
410
- var errors = [];
411
- angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) {
412
- angular.forEach($log[logLevel].logs, function(log) {
413
- angular.forEach(log, function (logItem) {
414
- errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' +
415
- (logItem.stack || ''));
416
- });
417
- });
418
- });
419
- if (errors.length) {
420
- errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or "+
421
- "an expected log message was not checked and removed:");
422
- errors.push('');
423
- throw new Error(errors.join('\n---------\n'));
424
- }
425
- };
426
-
427
- $log.reset();
428
- return $log;
429
- };
430
- };
431
-
432
-
433
- /**
434
- * @ngdoc service
435
- * @name $interval
436
- *
437
- * @description
438
- * Mock implementation of the $interval service.
439
- *
440
- * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
441
- * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
442
- * time.
443
- *
444
- * @param {function()} fn A function that should be called repeatedly.
445
- * @param {number} delay Number of milliseconds between each function call.
446
- * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
447
- * indefinitely.
448
- * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
449
- * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
450
- * @returns {promise} A promise which will be notified on each iteration.
451
- */
452
- angular.mock.$IntervalProvider = function() {
453
- this.$get = ['$rootScope', '$q',
454
- function($rootScope, $q) {
455
- var repeatFns = [],
456
- nextRepeatId = 0,
457
- now = 0;
458
-
459
- var $interval = function(fn, delay, count, invokeApply) {
460
- var deferred = $q.defer(),
461
- promise = deferred.promise,
462
- iteration = 0,
463
- skipApply = (angular.isDefined(invokeApply) && !invokeApply);
464
-
465
- count = (angular.isDefined(count)) ? count : 0;
466
- promise.then(null, null, fn);
467
-
468
- promise.$$intervalId = nextRepeatId;
469
-
470
- function tick() {
471
- deferred.notify(iteration++);
472
-
473
- if (count > 0 && iteration >= count) {
474
- var fnIndex;
475
- deferred.resolve(iteration);
476
-
477
- angular.forEach(repeatFns, function(fn, index) {
478
- if (fn.id === promise.$$intervalId) fnIndex = index;
479
- });
480
-
481
- if (fnIndex !== undefined) {
482
- repeatFns.splice(fnIndex, 1);
483
- }
484
- }
485
-
486
- if (!skipApply) $rootScope.$apply();
487
- }
488
-
489
- repeatFns.push({
490
- nextTime:(now + delay),
491
- delay: delay,
492
- fn: tick,
493
- id: nextRepeatId,
494
- deferred: deferred
495
- });
496
- repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
497
-
498
- nextRepeatId++;
499
- return promise;
500
- };
501
- /**
502
- * @ngdoc method
503
- * @name $interval#cancel
504
- *
505
- * @description
506
- * Cancels a task associated with the `promise`.
507
- *
508
- * @param {promise} promise A promise from calling the `$interval` function.
509
- * @returns {boolean} Returns `true` if the task was successfully cancelled.
510
- */
511
- $interval.cancel = function(promise) {
512
- if(!promise) return false;
513
- var fnIndex;
514
-
515
- angular.forEach(repeatFns, function(fn, index) {
516
- if (fn.id === promise.$$intervalId) fnIndex = index;
517
- });
518
-
519
- if (fnIndex !== undefined) {
520
- repeatFns[fnIndex].deferred.reject('canceled');
521
- repeatFns.splice(fnIndex, 1);
522
- return true;
523
- }
524
-
525
- return false;
526
- };
527
-
528
- /**
529
- * @ngdoc method
530
- * @name $interval#flush
531
- * @description
532
- *
533
- * Runs interval tasks scheduled to be run in the next `millis` milliseconds.
534
- *
535
- * @param {number=} millis maximum timeout amount to flush up until.
536
- *
537
- * @return {number} The amount of time moved forward.
538
- */
539
- $interval.flush = function(millis) {
540
- now += millis;
541
- while (repeatFns.length && repeatFns[0].nextTime <= now) {
542
- var task = repeatFns[0];
543
- task.fn();
544
- task.nextTime += task.delay;
545
- repeatFns.sort(function(a,b){ return a.nextTime - b.nextTime;});
546
- }
547
- return millis;
548
- };
549
-
550
- return $interval;
551
- }];
552
- };
553
-
554
-
555
- /* jshint -W101 */
556
- /* The R_ISO8061_STR regex is never going to fit into the 100 char limit!
557
- * This directive should go inside the anonymous function but a bug in JSHint means that it would
558
- * not be enacted early enough to prevent the warning.
559
- */
560
- var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
561
-
562
- function jsonStringToDate(string) {
563
- var match;
564
- if (match = string.match(R_ISO8061_STR)) {
565
- var date = new Date(0),
566
- tzHour = 0,
567
- tzMin = 0;
568
- if (match[9]) {
569
- tzHour = int(match[9] + match[10]);
570
- tzMin = int(match[9] + match[11]);
571
- }
572
- date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
573
- date.setUTCHours(int(match[4]||0) - tzHour,
574
- int(match[5]||0) - tzMin,
575
- int(match[6]||0),
576
- int(match[7]||0));
577
- return date;
578
- }
579
- return string;
580
- }
581
-
582
- function int(str) {
583
- return parseInt(str, 10);
584
- }
585
-
586
- function padNumber(num, digits, trim) {
587
- var neg = '';
588
- if (num < 0) {
589
- neg = '-';
590
- num = -num;
591
- }
592
- num = '' + num;
593
- while(num.length < digits) num = '0' + num;
594
- if (trim)
595
- num = num.substr(num.length - digits);
596
- return neg + num;
597
- }
598
-
599
-
600
- /**
601
- * @ngdoc type
602
- * @name angular.mock.TzDate
603
- * @description
604
- *
605
- * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
606
- *
607
- * Mock of the Date type which has its timezone specified via constructor arg.
608
- *
609
- * The main purpose is to create Date-like instances with timezone fixed to the specified timezone
610
- * offset, so that we can test code that depends on local timezone settings without dependency on
611
- * the time zone settings of the machine where the code is running.
612
- *
613
- * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
614
- * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
615
- *
616
- * @example
617
- * !!!! WARNING !!!!!
618
- * This is not a complete Date object so only methods that were implemented can be called safely.
619
- * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
620
- *
621
- * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
622
- * incomplete we might be missing some non-standard methods. This can result in errors like:
623
- * "Date.prototype.foo called on incompatible Object".
624
- *
625
- * ```js
626
- * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
627
- * newYearInBratislava.getTimezoneOffset() => -60;
628
- * newYearInBratislava.getFullYear() => 2010;
629
- * newYearInBratislava.getMonth() => 0;
630
- * newYearInBratislava.getDate() => 1;
631
- * newYearInBratislava.getHours() => 0;
632
- * newYearInBratislava.getMinutes() => 0;
633
- * newYearInBratislava.getSeconds() => 0;
634
- * ```
635
- *
636
- */
637
- angular.mock.TzDate = function (offset, timestamp) {
638
- var self = new Date(0);
639
- if (angular.isString(timestamp)) {
640
- var tsStr = timestamp;
641
-
642
- self.origDate = jsonStringToDate(timestamp);
643
-
644
- timestamp = self.origDate.getTime();
645
- if (isNaN(timestamp))
646
- throw {
647
- name: "Illegal Argument",
648
- message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
649
- };
650
- } else {
651
- self.origDate = new Date(timestamp);
652
- }
653
-
654
- var localOffset = new Date(timestamp).getTimezoneOffset();
655
- self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
656
- self.date = new Date(timestamp + self.offsetDiff);
657
-
658
- self.getTime = function() {
659
- return self.date.getTime() - self.offsetDiff;
660
- };
661
-
662
- self.toLocaleDateString = function() {
663
- return self.date.toLocaleDateString();
664
- };
665
-
666
- self.getFullYear = function() {
667
- return self.date.getFullYear();
668
- };
669
-
670
- self.getMonth = function() {
671
- return self.date.getMonth();
672
- };
673
-
674
- self.getDate = function() {
675
- return self.date.getDate();
676
- };
677
-
678
- self.getHours = function() {
679
- return self.date.getHours();
680
- };
681
-
682
- self.getMinutes = function() {
683
- return self.date.getMinutes();
684
- };
685
-
686
- self.getSeconds = function() {
687
- return self.date.getSeconds();
688
- };
689
-
690
- self.getMilliseconds = function() {
691
- return self.date.getMilliseconds();
692
- };
693
-
694
- self.getTimezoneOffset = function() {
695
- return offset * 60;
696
- };
697
-
698
- self.getUTCFullYear = function() {
699
- return self.origDate.getUTCFullYear();
700
- };
701
-
702
- self.getUTCMonth = function() {
703
- return self.origDate.getUTCMonth();
704
- };
705
-
706
- self.getUTCDate = function() {
707
- return self.origDate.getUTCDate();
708
- };
709
-
710
- self.getUTCHours = function() {
711
- return self.origDate.getUTCHours();
712
- };
713
-
714
- self.getUTCMinutes = function() {
715
- return self.origDate.getUTCMinutes();
716
- };
717
-
718
- self.getUTCSeconds = function() {
719
- return self.origDate.getUTCSeconds();
720
- };
721
-
722
- self.getUTCMilliseconds = function() {
723
- return self.origDate.getUTCMilliseconds();
724
- };
725
-
726
- self.getDay = function() {
727
- return self.date.getDay();
728
- };
729
-
730
- // provide this method only on browsers that already have it
731
- if (self.toISOString) {
732
- self.toISOString = function() {
733
- return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
734
- padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
735
- padNumber(self.origDate.getUTCDate(), 2) + 'T' +
736
- padNumber(self.origDate.getUTCHours(), 2) + ':' +
737
- padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
738
- padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
739
- padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
740
- };
741
- }
742
-
743
- //hide all methods not implemented in this mock that the Date prototype exposes
744
- var unimplementedMethods = ['getUTCDay',
745
- 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
746
- 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
747
- 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
748
- 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
749
- 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
750
-
751
- angular.forEach(unimplementedMethods, function(methodName) {
752
- self[methodName] = function() {
753
- throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
754
- };
755
- });
756
-
757
- return self;
758
- };
759
-
760
- //make "tzDateInstance instanceof Date" return true
761
- angular.mock.TzDate.prototype = Date.prototype;
762
- /* jshint +W101 */
763
-
764
- angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
765
-
766
- .config(['$provide', function($provide) {
767
-
768
- var reflowQueue = [];
769
- $provide.value('$$animateReflow', function(fn) {
770
- var index = reflowQueue.length;
771
- reflowQueue.push(fn);
772
- return function cancel() {
773
- reflowQueue.splice(index, 1);
774
- };
775
- });
776
-
777
- $provide.decorator('$animate', function($delegate, $$asyncCallback) {
778
- var animate = {
779
- queue : [],
780
- enabled : $delegate.enabled,
781
- triggerCallbacks : function() {
782
- $$asyncCallback.flush();
783
- },
784
- triggerReflow : function() {
785
- angular.forEach(reflowQueue, function(fn) {
786
- fn();
787
- });
788
- reflowQueue = [];
789
- }
790
- };
791
-
792
- angular.forEach(
793
- ['enter','leave','move','addClass','removeClass','setClass'], function(method) {
794
- animate[method] = function() {
795
- animate.queue.push({
796
- event : method,
797
- element : arguments[0],
798
- args : arguments
799
- });
800
- $delegate[method].apply($delegate, arguments);
801
- };
802
- });
803
-
804
- return animate;
805
- });
806
-
807
- }]);
808
-
809
-
810
- /**
811
- * @ngdoc function
812
- * @name angular.mock.dump
813
- * @description
814
- *
815
- * *NOTE*: this is not an injectable instance, just a globally available function.
816
- *
817
- * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
818
- * debugging.
819
- *
820
- * This method is also available on window, where it can be used to display objects on debug
821
- * console.
822
- *
823
- * @param {*} object - any object to turn into string.
824
- * @return {string} a serialized string of the argument
825
- */
826
- angular.mock.dump = function(object) {
827
- return serialize(object);
828
-
829
- function serialize(object) {
830
- var out;
831
-
832
- if (angular.isElement(object)) {
833
- object = angular.element(object);
834
- out = angular.element('<div></div>');
835
- angular.forEach(object, function(element) {
836
- out.append(angular.element(element).clone());
837
- });
838
- out = out.html();
839
- } else if (angular.isArray(object)) {
840
- out = [];
841
- angular.forEach(object, function(o) {
842
- out.push(serialize(o));
843
- });
844
- out = '[ ' + out.join(', ') + ' ]';
845
- } else if (angular.isObject(object)) {
846
- if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) {
847
- out = serializeScope(object);
848
- } else if (object instanceof Error) {
849
- out = object.stack || ('' + object.name + ': ' + object.message);
850
- } else {
851
- // TODO(i): this prevents methods being logged,
852
- // we should have a better way to serialize objects
853
- out = angular.toJson(object, true);
854
- }
855
- } else {
856
- out = String(object);
857
- }
858
-
859
- return out;
860
- }
861
-
862
- function serializeScope(scope, offset) {
863
- offset = offset || ' ';
864
- var log = [offset + 'Scope(' + scope.$id + '): {'];
865
- for ( var key in scope ) {
866
- if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) {
867
- log.push(' ' + key + ': ' + angular.toJson(scope[key]));
868
- }
869
- }
870
- var child = scope.$$childHead;
871
- while(child) {
872
- log.push(serializeScope(child, offset + ' '));
873
- child = child.$$nextSibling;
874
- }
875
- log.push('}');
876
- return log.join('\n' + offset);
877
- }
878
- };
879
-
880
- /**
881
- * @ngdoc service
882
- * @name $httpBackend
883
- * @description
884
- * Fake HTTP backend implementation suitable for unit testing applications that use the
885
- * {@link ng.$http $http service}.
886
- *
887
- * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less
888
- * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}.
889
- *
890
- * During unit testing, we want our unit tests to run quickly and have no external dependencies so
891
- * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or
892
- * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is
893
- * to verify whether a certain request has been sent or not, or alternatively just let the
894
- * application make requests, respond with pre-trained responses and assert that the end result is
895
- * what we expect it to be.
896
- *
897
- * This mock implementation can be used to respond with static or dynamic responses via the
898
- * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc).
899
- *
900
- * When an Angular application needs some data from a server, it calls the $http service, which
901
- * sends the request to a real server using $httpBackend service. With dependency injection, it is
902
- * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify
903
- * the requests and respond with some testing data without sending a request to a real server.
904
- *
905
- * There are two ways to specify what test data should be returned as http responses by the mock
906
- * backend when the code under test makes http requests:
907
- *
908
- * - `$httpBackend.expect` - specifies a request expectation
909
- * - `$httpBackend.when` - specifies a backend definition
910
- *
911
- *
912
- * # Request Expectations vs Backend Definitions
913
- *
914
- * Request expectations provide a way to make assertions about requests made by the application and
915
- * to define responses for those requests. The test will fail if the expected requests are not made
916
- * or they are made in the wrong order.
917
- *
918
- * Backend definitions allow you to define a fake backend for your application which doesn't assert
919
- * if a particular request was made or not, it just returns a trained response if a request is made.
920
- * The test will pass whether or not the request gets made during testing.
921
- *
922
- *
923
- * <table class="table">
924
- * <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr>
925
- * <tr>
926
- * <th>Syntax</th>
927
- * <td>.expect(...).respond(...)</td>
928
- * <td>.when(...).respond(...)</td>
929
- * </tr>
930
- * <tr>
931
- * <th>Typical usage</th>
932
- * <td>strict unit tests</td>
933
- * <td>loose (black-box) unit testing</td>
934
- * </tr>
935
- * <tr>
936
- * <th>Fulfills multiple requests</th>
937
- * <td>NO</td>
938
- * <td>YES</td>
939
- * </tr>
940
- * <tr>
941
- * <th>Order of requests matters</th>
942
- * <td>YES</td>
943
- * <td>NO</td>
944
- * </tr>
945
- * <tr>
946
- * <th>Request required</th>
947
- * <td>YES</td>
948
- * <td>NO</td>
949
- * </tr>
950
- * <tr>
951
- * <th>Response required</th>
952
- * <td>optional (see below)</td>
953
- * <td>YES</td>
954
- * </tr>
955
- * </table>
956
- *
957
- * In cases where both backend definitions and request expectations are specified during unit
958
- * testing, the request expectations are evaluated first.
959
- *
960
- * If a request expectation has no response specified, the algorithm will search your backend
961
- * definitions for an appropriate response.
962
- *
963
- * If a request didn't match any expectation or if the expectation doesn't have the response
964
- * defined, the backend definitions are evaluated in sequential order to see if any of them match
965
- * the request. The response from the first matched definition is returned.
966
- *
967
- *
968
- * # Flushing HTTP requests
969
- *
970
- * The $httpBackend used in production always responds to requests asynchronously. If we preserved
971
- * this behavior in unit testing, we'd have to create async unit tests, which are hard to write,
972
- * to follow and to maintain. But neither can the testing mock respond synchronously; that would
973
- * change the execution of the code under test. For this reason, the mock $httpBackend has a
974
- * `flush()` method, which allows the test to explicitly flush pending requests. This preserves
975
- * the async api of the backend, while allowing the test to execute synchronously.
976
- *
977
- *
978
- * # Unit testing with mock $httpBackend
979
- * The following code shows how to setup and use the mock backend when unit testing a controller.
980
- * First we create the controller under test:
981
- *
982
- ```js
983
- // The controller code
984
- function MyController($scope, $http) {
985
- var authToken;
986
-
987
- $http.get('/auth.py').success(function(data, status, headers) {
988
- authToken = headers('A-Token');
989
- $scope.user = data;
990
- });
991
-
992
- $scope.saveMessage = function(message) {
993
- var headers = { 'Authorization': authToken };
994
- $scope.status = 'Saving...';
995
-
996
- $http.post('/add-msg.py', message, { headers: headers } ).success(function(response) {
997
- $scope.status = '';
998
- }).error(function() {
999
- $scope.status = 'ERROR!';
1000
- });
1001
- };
1002
- }
1003
- ```
1004
- *
1005
- * Now we setup the mock backend and create the test specs:
1006
- *
1007
- ```js
1008
- // testing controller
1009
- describe('MyController', function() {
1010
- var $httpBackend, $rootScope, createController;
1011
-
1012
- beforeEach(inject(function($injector) {
1013
- // Set up the mock http service responses
1014
- $httpBackend = $injector.get('$httpBackend');
1015
- // backend definition common for all tests
1016
- $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
1017
-
1018
- // Get hold of a scope (i.e. the root scope)
1019
- $rootScope = $injector.get('$rootScope');
1020
- // The $controller service is used to create instances of controllers
1021
- var $controller = $injector.get('$controller');
1022
-
1023
- createController = function() {
1024
- return $controller('MyController', {'$scope' : $rootScope });
1025
- };
1026
- }));
1027
-
1028
-
1029
- afterEach(function() {
1030
- $httpBackend.verifyNoOutstandingExpectation();
1031
- $httpBackend.verifyNoOutstandingRequest();
1032
- });
1033
-
1034
-
1035
- it('should fetch authentication token', function() {
1036
- $httpBackend.expectGET('/auth.py');
1037
- var controller = createController();
1038
- $httpBackend.flush();
1039
- });
1040
-
1041
-
1042
- it('should send msg to server', function() {
1043
- var controller = createController();
1044
- $httpBackend.flush();
1045
-
1046
- // now you don’t care about the authentication, but
1047
- // the controller will still send the request and
1048
- // $httpBackend will respond without you having to
1049
- // specify the expectation and response for this request
1050
-
1051
- $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
1052
- $rootScope.saveMessage('message content');
1053
- expect($rootScope.status).toBe('Saving...');
1054
- $httpBackend.flush();
1055
- expect($rootScope.status).toBe('');
1056
- });
1057
-
1058
-
1059
- it('should send auth header', function() {
1060
- var controller = createController();
1061
- $httpBackend.flush();
1062
-
1063
- $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
1064
- // check if the header was send, if it wasn't the expectation won't
1065
- // match the request and the test will fail
1066
- return headers['Authorization'] == 'xxx';
1067
- }).respond(201, '');
1068
-
1069
- $rootScope.saveMessage('whatever');
1070
- $httpBackend.flush();
1071
- });
1072
- });
1073
- ```
1074
- */
1075
- angular.mock.$HttpBackendProvider = function() {
1076
- this.$get = ['$rootScope', createHttpBackendMock];
1077
- };
1078
-
1079
- /**
1080
- * General factory function for $httpBackend mock.
1081
- * Returns instance for unit testing (when no arguments specified):
1082
- * - passing through is disabled
1083
- * - auto flushing is disabled
1084
- *
1085
- * Returns instance for e2e testing (when `$delegate` and `$browser` specified):
1086
- * - passing through (delegating request to real backend) is enabled
1087
- * - auto flushing is enabled
1088
- *
1089
- * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified)
1090
- * @param {Object=} $browser Auto-flushing enabled if specified
1091
- * @return {Object} Instance of $httpBackend mock
1092
- */
1093
- function createHttpBackendMock($rootScope, $delegate, $browser) {
1094
- var definitions = [],
1095
- expectations = [],
1096
- responses = [],
1097
- responsesPush = angular.bind(responses, responses.push),
1098
- copy = angular.copy;
1099
-
1100
- function createResponse(status, data, headers, statusText) {
1101
- if (angular.isFunction(status)) return status;
1102
-
1103
- return function() {
1104
- return angular.isNumber(status)
1105
- ? [status, data, headers, statusText]
1106
- : [200, status, data];
1107
- };
1108
- }
1109
-
1110
- // TODO(vojta): change params to: method, url, data, headers, callback
1111
- function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) {
1112
- var xhr = new MockXhr(),
1113
- expectation = expectations[0],
1114
- wasExpected = false;
1115
-
1116
- function prettyPrint(data) {
1117
- return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
1118
- ? data
1119
- : angular.toJson(data);
1120
- }
1121
-
1122
- function wrapResponse(wrapped) {
1123
- if (!$browser && timeout && timeout.then) timeout.then(handleTimeout);
1124
-
1125
- return handleResponse;
1126
-
1127
- function handleResponse() {
1128
- var response = wrapped.response(method, url, data, headers);
1129
- xhr.$$respHeaders = response[2];
1130
- callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(),
1131
- copy(response[3] || ''));
1132
- }
1133
-
1134
- function handleTimeout() {
1135
- for (var i = 0, ii = responses.length; i < ii; i++) {
1136
- if (responses[i] === handleResponse) {
1137
- responses.splice(i, 1);
1138
- callback(-1, undefined, '');
1139
- break;
1140
- }
1141
- }
1142
- }
1143
- }
1144
-
1145
- if (expectation && expectation.match(method, url)) {
1146
- if (!expectation.matchData(data))
1147
- throw new Error('Expected ' + expectation + ' with different data\n' +
1148
- 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data);
1149
-
1150
- if (!expectation.matchHeaders(headers))
1151
- throw new Error('Expected ' + expectation + ' with different headers\n' +
1152
- 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' +
1153
- prettyPrint(headers));
1154
-
1155
- expectations.shift();
1156
-
1157
- if (expectation.response) {
1158
- responses.push(wrapResponse(expectation));
1159
- return;
1160
- }
1161
- wasExpected = true;
1162
- }
1163
-
1164
- var i = -1, definition;
1165
- while ((definition = definitions[++i])) {
1166
- if (definition.match(method, url, data, headers || {})) {
1167
- if (definition.response) {
1168
- // if $browser specified, we do auto flush all requests
1169
- ($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
1170
- } else if (definition.passThrough) {
1171
- $delegate(method, url, data, callback, headers, timeout, withCredentials);
1172
- } else throw new Error('No response defined !');
1173
- return;
1174
- }
1175
- }
1176
- throw wasExpected ?
1177
- new Error('No response defined !') :
1178
- new Error('Unexpected request: ' + method + ' ' + url + '\n' +
1179
- (expectation ? 'Expected ' + expectation : 'No more request expected'));
1180
- }
1181
-
1182
- /**
1183
- * @ngdoc method
1184
- * @name $httpBackend#when
1185
- * @description
1186
- * Creates a new backend definition.
1187
- *
1188
- * @param {string} method HTTP method.
1189
- * @param {string|RegExp} url HTTP url.
1190
- * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1191
- * data string and returns true if the data is as expected.
1192
- * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1193
- * object and returns true if the headers match the current definition.
1194
- * @returns {requestHandler} Returns an object with `respond` method that controls how a matched
1195
- * request is handled.
1196
- *
1197
- * - respond –
1198
- * `{function([status,] data[, headers, statusText])
1199
- * | function(function(method, url, data, headers)}`
1200
- * – The respond method takes a set of static data to be returned or a function that can
1201
- * return an array containing response status (number), response data (string), response
1202
- * headers (Object), and the text for the status (string).
1203
- */
1204
- $httpBackend.when = function(method, url, data, headers) {
1205
- var definition = new MockHttpExpectation(method, url, data, headers),
1206
- chain = {
1207
- respond: function(status, data, headers, statusText) {
1208
- definition.response = createResponse(status, data, headers, statusText);
1209
- }
1210
- };
1211
-
1212
- if ($browser) {
1213
- chain.passThrough = function() {
1214
- definition.passThrough = true;
1215
- };
1216
- }
1217
-
1218
- definitions.push(definition);
1219
- return chain;
1220
- };
1221
-
1222
- /**
1223
- * @ngdoc method
1224
- * @name $httpBackend#whenGET
1225
- * @description
1226
- * Creates a new backend definition for GET requests. For more info see `when()`.
1227
- *
1228
- * @param {string|RegExp} url HTTP url.
1229
- * @param {(Object|function(Object))=} headers HTTP headers.
1230
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1231
- * request is handled.
1232
- */
1233
-
1234
- /**
1235
- * @ngdoc method
1236
- * @name $httpBackend#whenHEAD
1237
- * @description
1238
- * Creates a new backend definition for HEAD requests. For more info see `when()`.
1239
- *
1240
- * @param {string|RegExp} url HTTP url.
1241
- * @param {(Object|function(Object))=} headers HTTP headers.
1242
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1243
- * request is handled.
1244
- */
1245
-
1246
- /**
1247
- * @ngdoc method
1248
- * @name $httpBackend#whenDELETE
1249
- * @description
1250
- * Creates a new backend definition for DELETE requests. For more info see `when()`.
1251
- *
1252
- * @param {string|RegExp} url HTTP url.
1253
- * @param {(Object|function(Object))=} headers HTTP headers.
1254
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1255
- * request is handled.
1256
- */
1257
-
1258
- /**
1259
- * @ngdoc method
1260
- * @name $httpBackend#whenPOST
1261
- * @description
1262
- * Creates a new backend definition for POST requests. For more info see `when()`.
1263
- *
1264
- * @param {string|RegExp} url HTTP url.
1265
- * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1266
- * data string and returns true if the data is as expected.
1267
- * @param {(Object|function(Object))=} headers HTTP headers.
1268
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1269
- * request is handled.
1270
- */
1271
-
1272
- /**
1273
- * @ngdoc method
1274
- * @name $httpBackend#whenPUT
1275
- * @description
1276
- * Creates a new backend definition for PUT requests. For more info see `when()`.
1277
- *
1278
- * @param {string|RegExp} url HTTP url.
1279
- * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
1280
- * data string and returns true if the data is as expected.
1281
- * @param {(Object|function(Object))=} headers HTTP headers.
1282
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1283
- * request is handled.
1284
- */
1285
-
1286
- /**
1287
- * @ngdoc method
1288
- * @name $httpBackend#whenJSONP
1289
- * @description
1290
- * Creates a new backend definition for JSONP requests. For more info see `when()`.
1291
- *
1292
- * @param {string|RegExp} url HTTP url.
1293
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1294
- * request is handled.
1295
- */
1296
- createShortMethods('when');
1297
-
1298
-
1299
- /**
1300
- * @ngdoc method
1301
- * @name $httpBackend#expect
1302
- * @description
1303
- * Creates a new request expectation.
1304
- *
1305
- * @param {string} method HTTP method.
1306
- * @param {string|RegExp} url HTTP url.
1307
- * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1308
- * receives data string and returns true if the data is as expected, or Object if request body
1309
- * is in JSON format.
1310
- * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1311
- * object and returns true if the headers match the current expectation.
1312
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1313
- * request is handled.
1314
- *
1315
- * - respond –
1316
- * `{function([status,] data[, headers, statusText])
1317
- * | function(function(method, url, data, headers)}`
1318
- * – The respond method takes a set of static data to be returned or a function that can
1319
- * return an array containing response status (number), response data (string), response
1320
- * headers (Object), and the text for the status (string).
1321
- */
1322
- $httpBackend.expect = function(method, url, data, headers) {
1323
- var expectation = new MockHttpExpectation(method, url, data, headers);
1324
- expectations.push(expectation);
1325
- return {
1326
- respond: function (status, data, headers, statusText) {
1327
- expectation.response = createResponse(status, data, headers, statusText);
1328
- }
1329
- };
1330
- };
1331
-
1332
-
1333
- /**
1334
- * @ngdoc method
1335
- * @name $httpBackend#expectGET
1336
- * @description
1337
- * Creates a new request expectation for GET requests. For more info see `expect()`.
1338
- *
1339
- * @param {string|RegExp} url HTTP url.
1340
- * @param {Object=} headers HTTP headers.
1341
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1342
- * request is handled. See #expect for more info.
1343
- */
1344
-
1345
- /**
1346
- * @ngdoc method
1347
- * @name $httpBackend#expectHEAD
1348
- * @description
1349
- * Creates a new request expectation for HEAD requests. For more info see `expect()`.
1350
- *
1351
- * @param {string|RegExp} url HTTP url.
1352
- * @param {Object=} headers HTTP headers.
1353
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1354
- * request is handled.
1355
- */
1356
-
1357
- /**
1358
- * @ngdoc method
1359
- * @name $httpBackend#expectDELETE
1360
- * @description
1361
- * Creates a new request expectation for DELETE requests. For more info see `expect()`.
1362
- *
1363
- * @param {string|RegExp} url HTTP url.
1364
- * @param {Object=} headers HTTP headers.
1365
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1366
- * request is handled.
1367
- */
1368
-
1369
- /**
1370
- * @ngdoc method
1371
- * @name $httpBackend#expectPOST
1372
- * @description
1373
- * Creates a new request expectation for POST requests. For more info see `expect()`.
1374
- *
1375
- * @param {string|RegExp} url HTTP url.
1376
- * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1377
- * receives data string and returns true if the data is as expected, or Object if request body
1378
- * is in JSON format.
1379
- * @param {Object=} headers HTTP headers.
1380
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1381
- * request is handled.
1382
- */
1383
-
1384
- /**
1385
- * @ngdoc method
1386
- * @name $httpBackend#expectPUT
1387
- * @description
1388
- * Creates a new request expectation for PUT requests. For more info see `expect()`.
1389
- *
1390
- * @param {string|RegExp} url HTTP url.
1391
- * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1392
- * receives data string and returns true if the data is as expected, or Object if request body
1393
- * is in JSON format.
1394
- * @param {Object=} headers HTTP headers.
1395
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1396
- * request is handled.
1397
- */
1398
-
1399
- /**
1400
- * @ngdoc method
1401
- * @name $httpBackend#expectPATCH
1402
- * @description
1403
- * Creates a new request expectation for PATCH requests. For more info see `expect()`.
1404
- *
1405
- * @param {string|RegExp} url HTTP url.
1406
- * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that
1407
- * receives data string and returns true if the data is as expected, or Object if request body
1408
- * is in JSON format.
1409
- * @param {Object=} headers HTTP headers.
1410
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1411
- * request is handled.
1412
- */
1413
-
1414
- /**
1415
- * @ngdoc method
1416
- * @name $httpBackend#expectJSONP
1417
- * @description
1418
- * Creates a new request expectation for JSONP requests. For more info see `expect()`.
1419
- *
1420
- * @param {string|RegExp} url HTTP url.
1421
- * @returns {requestHandler} Returns an object with `respond` method that control how a matched
1422
- * request is handled.
1423
- */
1424
- createShortMethods('expect');
1425
-
1426
-
1427
- /**
1428
- * @ngdoc method
1429
- * @name $httpBackend#flush
1430
- * @description
1431
- * Flushes all pending requests using the trained responses.
1432
- *
1433
- * @param {number=} count Number of responses to flush (in the order they arrived). If undefined,
1434
- * all pending requests will be flushed. If there are no pending requests when the flush method
1435
- * is called an exception is thrown (as this typically a sign of programming error).
1436
- */
1437
- $httpBackend.flush = function(count) {
1438
- $rootScope.$digest();
1439
- if (!responses.length) throw new Error('No pending request to flush !');
1440
-
1441
- if (angular.isDefined(count)) {
1442
- while (count--) {
1443
- if (!responses.length) throw new Error('No more pending request to flush !');
1444
- responses.shift()();
1445
- }
1446
- } else {
1447
- while (responses.length) {
1448
- responses.shift()();
1449
- }
1450
- }
1451
- $httpBackend.verifyNoOutstandingExpectation();
1452
- };
1453
-
1454
-
1455
- /**
1456
- * @ngdoc method
1457
- * @name $httpBackend#verifyNoOutstandingExpectation
1458
- * @description
1459
- * Verifies that all of the requests defined via the `expect` api were made. If any of the
1460
- * requests were not made, verifyNoOutstandingExpectation throws an exception.
1461
- *
1462
- * Typically, you would call this method following each test case that asserts requests using an
1463
- * "afterEach" clause.
1464
- *
1465
- * ```js
1466
- * afterEach($httpBackend.verifyNoOutstandingExpectation);
1467
- * ```
1468
- */
1469
- $httpBackend.verifyNoOutstandingExpectation = function() {
1470
- $rootScope.$digest();
1471
- if (expectations.length) {
1472
- throw new Error('Unsatisfied requests: ' + expectations.join(', '));
1473
- }
1474
- };
1475
-
1476
-
1477
- /**
1478
- * @ngdoc method
1479
- * @name $httpBackend#verifyNoOutstandingRequest
1480
- * @description
1481
- * Verifies that there are no outstanding requests that need to be flushed.
1482
- *
1483
- * Typically, you would call this method following each test case that asserts requests using an
1484
- * "afterEach" clause.
1485
- *
1486
- * ```js
1487
- * afterEach($httpBackend.verifyNoOutstandingRequest);
1488
- * ```
1489
- */
1490
- $httpBackend.verifyNoOutstandingRequest = function() {
1491
- if (responses.length) {
1492
- throw new Error('Unflushed requests: ' + responses.length);
1493
- }
1494
- };
1495
-
1496
-
1497
- /**
1498
- * @ngdoc method
1499
- * @name $httpBackend#resetExpectations
1500
- * @description
1501
- * Resets all request expectations, but preserves all backend definitions. Typically, you would
1502
- * call resetExpectations during a multiple-phase test when you want to reuse the same instance of
1503
- * $httpBackend mock.
1504
- */
1505
- $httpBackend.resetExpectations = function() {
1506
- expectations.length = 0;
1507
- responses.length = 0;
1508
- };
1509
-
1510
- return $httpBackend;
1511
-
1512
-
1513
- function createShortMethods(prefix) {
1514
- angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) {
1515
- $httpBackend[prefix + method] = function(url, headers) {
1516
- return $httpBackend[prefix](method, url, undefined, headers);
1517
- };
1518
- });
1519
-
1520
- angular.forEach(['PUT', 'POST', 'PATCH'], function(method) {
1521
- $httpBackend[prefix + method] = function(url, data, headers) {
1522
- return $httpBackend[prefix](method, url, data, headers);
1523
- };
1524
- });
1525
- }
1526
- }
1527
-
1528
- function MockHttpExpectation(method, url, data, headers) {
1529
-
1530
- this.data = data;
1531
- this.headers = headers;
1532
-
1533
- this.match = function(m, u, d, h) {
1534
- if (method != m) return false;
1535
- if (!this.matchUrl(u)) return false;
1536
- if (angular.isDefined(d) && !this.matchData(d)) return false;
1537
- if (angular.isDefined(h) && !this.matchHeaders(h)) return false;
1538
- return true;
1539
- };
1540
-
1541
- this.matchUrl = function(u) {
1542
- if (!url) return true;
1543
- if (angular.isFunction(url.test)) return url.test(u);
1544
- return url == u;
1545
- };
1546
-
1547
- this.matchHeaders = function(h) {
1548
- if (angular.isUndefined(headers)) return true;
1549
- if (angular.isFunction(headers)) return headers(h);
1550
- return angular.equals(headers, h);
1551
- };
1552
-
1553
- this.matchData = function(d) {
1554
- if (angular.isUndefined(data)) return true;
1555
- if (data && angular.isFunction(data.test)) return data.test(d);
1556
- if (data && angular.isFunction(data)) return data(d);
1557
- if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d));
1558
- return data == d;
1559
- };
1560
-
1561
- this.toString = function() {
1562
- return method + ' ' + url;
1563
- };
1564
- }
1565
-
1566
- function createMockXhr() {
1567
- return new MockXhr();
1568
- }
1569
-
1570
- function MockXhr() {
1571
-
1572
- // hack for testing $http, $httpBackend
1573
- MockXhr.$$lastInstance = this;
1574
-
1575
- this.open = function(method, url, async) {
1576
- this.$$method = method;
1577
- this.$$url = url;
1578
- this.$$async = async;
1579
- this.$$reqHeaders = {};
1580
- this.$$respHeaders = {};
1581
- };
1582
-
1583
- this.send = function(data) {
1584
- this.$$data = data;
1585
- };
1586
-
1587
- this.setRequestHeader = function(key, value) {
1588
- this.$$reqHeaders[key] = value;
1589
- };
1590
-
1591
- this.getResponseHeader = function(name) {
1592
- // the lookup must be case insensitive,
1593
- // that's why we try two quick lookups first and full scan last
1594
- var header = this.$$respHeaders[name];
1595
- if (header) return header;
1596
-
1597
- name = angular.lowercase(name);
1598
- header = this.$$respHeaders[name];
1599
- if (header) return header;
1600
-
1601
- header = undefined;
1602
- angular.forEach(this.$$respHeaders, function(headerVal, headerName) {
1603
- if (!header && angular.lowercase(headerName) == name) header = headerVal;
1604
- });
1605
- return header;
1606
- };
1607
-
1608
- this.getAllResponseHeaders = function() {
1609
- var lines = [];
1610
-
1611
- angular.forEach(this.$$respHeaders, function(value, key) {
1612
- lines.push(key + ': ' + value);
1613
- });
1614
- return lines.join('\n');
1615
- };
1616
-
1617
- this.abort = angular.noop;
1618
- }
1619
-
1620
-
1621
- /**
1622
- * @ngdoc service
1623
- * @name $timeout
1624
- * @description
1625
- *
1626
- * This service is just a simple decorator for {@link ng.$timeout $timeout} service
1627
- * that adds a "flush" and "verifyNoPendingTasks" methods.
1628
- */
1629
-
1630
- angular.mock.$TimeoutDecorator = function($delegate, $browser) {
1631
-
1632
- /**
1633
- * @ngdoc method
1634
- * @name $timeout#flush
1635
- * @description
1636
- *
1637
- * Flushes the queue of pending tasks.
1638
- *
1639
- * @param {number=} delay maximum timeout amount to flush up until
1640
- */
1641
- $delegate.flush = function(delay) {
1642
- $browser.defer.flush(delay);
1643
- };
1644
-
1645
- /**
1646
- * @ngdoc method
1647
- * @name $timeout#verifyNoPendingTasks
1648
- * @description
1649
- *
1650
- * Verifies that there are no pending tasks that need to be flushed.
1651
- */
1652
- $delegate.verifyNoPendingTasks = function() {
1653
- if ($browser.deferredFns.length) {
1654
- throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
1655
- formatPendingTasksAsString($browser.deferredFns));
1656
- }
1657
- };
1658
-
1659
- function formatPendingTasksAsString(tasks) {
1660
- var result = [];
1661
- angular.forEach(tasks, function(task) {
1662
- result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
1663
- });
1664
-
1665
- return result.join(', ');
1666
- }
1667
-
1668
- return $delegate;
1669
- };
1670
-
1671
- angular.mock.$RAFDecorator = function($delegate) {
1672
- var queue = [];
1673
- var rafFn = function(fn) {
1674
- var index = queue.length;
1675
- queue.push(fn);
1676
- return function() {
1677
- queue.splice(index, 1);
1678
- };
1679
- };
1680
-
1681
- rafFn.supported = $delegate.supported;
1682
-
1683
- rafFn.flush = function() {
1684
- if(queue.length === 0) {
1685
- throw new Error('No rAF callbacks present');
1686
- }
1687
-
1688
- var length = queue.length;
1689
- for(var i=0;i<length;i++) {
1690
- queue[i]();
1691
- }
1692
-
1693
- queue = [];
1694
- };
1695
-
1696
- return rafFn;
1697
- };
1698
-
1699
- angular.mock.$AsyncCallbackDecorator = function($delegate) {
1700
- var callbacks = [];
1701
- var addFn = function(fn) {
1702
- callbacks.push(fn);
1703
- };
1704
- addFn.flush = function() {
1705
- angular.forEach(callbacks, function(fn) {
1706
- fn();
1707
- });
1708
- callbacks = [];
1709
- };
1710
- return addFn;
1711
- };
1712
-
1713
- /**
1714
- *
1715
- */
1716
- angular.mock.$RootElementProvider = function() {
1717
- this.$get = function() {
1718
- return angular.element('<div ng-app></div>');
1719
- };
1720
- };
1721
-
1722
- /**
1723
- * @ngdoc module
1724
- * @name ngMock
1725
- * @packageName angular-mocks
1726
- * @description
1727
- *
1728
- * # ngMock
1729
- *
1730
- * The `ngMock` module provides support to inject and mock Angular services into unit tests.
1731
- * In addition, ngMock also extends various core ng services such that they can be
1732
- * inspected and controlled in a synchronous manner within test code.
1733
- *
1734
- *
1735
- * <div doc-module-components="ngMock"></div>
1736
- *
1737
- */
1738
- angular.module('ngMock', ['ng']).provider({
1739
- $browser: angular.mock.$BrowserProvider,
1740
- $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
1741
- $log: angular.mock.$LogProvider,
1742
- $interval: angular.mock.$IntervalProvider,
1743
- $httpBackend: angular.mock.$HttpBackendProvider,
1744
- $rootElement: angular.mock.$RootElementProvider
1745
- }).config(['$provide', function($provide) {
1746
- $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
1747
- $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
1748
- $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
1749
- }]);
1750
-
1751
- /**
1752
- * @ngdoc module
1753
- * @name ngMockE2E
1754
- * @module ngMockE2E
1755
- * @packageName angular-mocks
1756
- * @description
1757
- *
1758
- * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing.
1759
- * Currently there is only one mock present in this module -
1760
- * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
1761
- */
1762
- angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
1763
- $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
1764
- }]);
1765
-
1766
- /**
1767
- * @ngdoc service
1768
- * @name $httpBackend
1769
- * @module ngMockE2E
1770
- * @description
1771
- * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of
1772
- * applications that use the {@link ng.$http $http service}.
1773
- *
1774
- * *Note*: For fake http backend implementation suitable for unit testing please see
1775
- * {@link ngMock.$httpBackend unit-testing $httpBackend mock}.
1776
- *
1777
- * This implementation can be used to respond with static or dynamic responses via the `when` api
1778
- * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the
1779
- * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch
1780
- * templates from a webserver).
1781
- *
1782
- * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application
1783
- * is being developed with the real backend api replaced with a mock, it is often desirable for
1784
- * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch
1785
- * templates or static files from the webserver). To configure the backend with this behavior
1786
- * use the `passThrough` request handler of `when` instead of `respond`.
1787
- *
1788
- * Additionally, we don't want to manually have to flush mocked out requests like we do during unit
1789
- * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests
1790
- * automatically, closely simulating the behavior of the XMLHttpRequest object.
1791
- *
1792
- * To setup the application to run with this http backend, you have to create a module that depends
1793
- * on the `ngMockE2E` and your application modules and defines the fake backend:
1794
- *
1795
- * ```js
1796
- * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
1797
- * myAppDev.run(function($httpBackend) {
1798
- * phones = [{name: 'phone1'}, {name: 'phone2'}];
1799
- *
1800
- * // returns the current list of phones
1801
- * $httpBackend.whenGET('/phones').respond(phones);
1802
- *
1803
- * // adds a new phone to the phones array
1804
- * $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
1805
- * var phone = angular.fromJson(data);
1806
- * phones.push(phone);
1807
- * return [200, phone, {}];
1808
- * });
1809
- * $httpBackend.whenGET(/^\/templates\//).passThrough();
1810
- * //...
1811
- * });
1812
- * ```
1813
- *
1814
- * Afterwards, bootstrap your app with this new module.
1815
- */
1816
-
1817
- /**
1818
- * @ngdoc method
1819
- * @name $httpBackend#when
1820
- * @module ngMockE2E
1821
- * @description
1822
- * Creates a new backend definition.
1823
- *
1824
- * @param {string} method HTTP method.
1825
- * @param {string|RegExp} url HTTP url.
1826
- * @param {(string|RegExp)=} data HTTP request body.
1827
- * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
1828
- * object and returns true if the headers match the current definition.
1829
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1830
- * control how a matched request is handled.
1831
- *
1832
- * - respond –
1833
- * `{function([status,] data[, headers, statusText])
1834
- * | function(function(method, url, data, headers)}`
1835
- * – The respond method takes a set of static data to be returned or a function that can return
1836
- * an array containing response status (number), response data (string), response headers
1837
- * (Object), and the text for the status (string).
1838
- * - passThrough – `{function()}` – Any request matching a backend definition with
1839
- * `passThrough` handler will be passed through to the real backend (an XHR request will be made
1840
- * to the server.)
1841
- */
1842
-
1843
- /**
1844
- * @ngdoc method
1845
- * @name $httpBackend#whenGET
1846
- * @module ngMockE2E
1847
- * @description
1848
- * Creates a new backend definition for GET requests. For more info see `when()`.
1849
- *
1850
- * @param {string|RegExp} url HTTP url.
1851
- * @param {(Object|function(Object))=} headers HTTP headers.
1852
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1853
- * control how a matched request is handled.
1854
- */
1855
-
1856
- /**
1857
- * @ngdoc method
1858
- * @name $httpBackend#whenHEAD
1859
- * @module ngMockE2E
1860
- * @description
1861
- * Creates a new backend definition for HEAD requests. For more info see `when()`.
1862
- *
1863
- * @param {string|RegExp} url HTTP url.
1864
- * @param {(Object|function(Object))=} headers HTTP headers.
1865
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1866
- * control how a matched request is handled.
1867
- */
1868
-
1869
- /**
1870
- * @ngdoc method
1871
- * @name $httpBackend#whenDELETE
1872
- * @module ngMockE2E
1873
- * @description
1874
- * Creates a new backend definition for DELETE requests. For more info see `when()`.
1875
- *
1876
- * @param {string|RegExp} url HTTP url.
1877
- * @param {(Object|function(Object))=} headers HTTP headers.
1878
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1879
- * control how a matched request is handled.
1880
- */
1881
-
1882
- /**
1883
- * @ngdoc method
1884
- * @name $httpBackend#whenPOST
1885
- * @module ngMockE2E
1886
- * @description
1887
- * Creates a new backend definition for POST requests. For more info see `when()`.
1888
- *
1889
- * @param {string|RegExp} url HTTP url.
1890
- * @param {(string|RegExp)=} data HTTP request body.
1891
- * @param {(Object|function(Object))=} headers HTTP headers.
1892
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1893
- * control how a matched request is handled.
1894
- */
1895
-
1896
- /**
1897
- * @ngdoc method
1898
- * @name $httpBackend#whenPUT
1899
- * @module ngMockE2E
1900
- * @description
1901
- * Creates a new backend definition for PUT requests. For more info see `when()`.
1902
- *
1903
- * @param {string|RegExp} url HTTP url.
1904
- * @param {(string|RegExp)=} data HTTP request body.
1905
- * @param {(Object|function(Object))=} headers HTTP headers.
1906
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1907
- * control how a matched request is handled.
1908
- */
1909
-
1910
- /**
1911
- * @ngdoc method
1912
- * @name $httpBackend#whenPATCH
1913
- * @module ngMockE2E
1914
- * @description
1915
- * Creates a new backend definition for PATCH requests. For more info see `when()`.
1916
- *
1917
- * @param {string|RegExp} url HTTP url.
1918
- * @param {(string|RegExp)=} data HTTP request body.
1919
- * @param {(Object|function(Object))=} headers HTTP headers.
1920
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1921
- * control how a matched request is handled.
1922
- */
1923
-
1924
- /**
1925
- * @ngdoc method
1926
- * @name $httpBackend#whenJSONP
1927
- * @module ngMockE2E
1928
- * @description
1929
- * Creates a new backend definition for JSONP requests. For more info see `when()`.
1930
- *
1931
- * @param {string|RegExp} url HTTP url.
1932
- * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that
1933
- * control how a matched request is handled.
1934
- */
1935
- angular.mock.e2e = {};
1936
- angular.mock.e2e.$httpBackendDecorator =
1937
- ['$rootScope', '$delegate', '$browser', createHttpBackendMock];
1938
-
1939
-
1940
- angular.mock.clearDataCache = function() {
1941
- var key,
1942
- cache = angular.element.cache;
1943
-
1944
- for(key in cache) {
1945
- if (Object.prototype.hasOwnProperty.call(cache,key)) {
1946
- var handle = cache[key].handle;
1947
-
1948
- handle && angular.element(handle.elem).off();
1949
- delete cache[key];
1950
- }
1951
- }
1952
- };
1953
-
1954
-
1955
- if(window.jasmine || window.mocha) {
1956
-
1957
- var currentSpec = null,
1958
- isSpecRunning = function() {
1959
- return !!currentSpec;
1960
- };
1961
-
1962
-
1963
- (window.beforeEach || window.setup)(function() {
1964
- currentSpec = this;
1965
- });
1966
-
1967
- (window.afterEach || window.teardown)(function() {
1968
- var injector = currentSpec.$injector;
1969
-
1970
- angular.forEach(currentSpec.$modules, function(module) {
1971
- if (module && module.$$hashKey) {
1972
- module.$$hashKey = undefined;
1973
- }
1974
- });
1975
-
1976
- currentSpec.$injector = null;
1977
- currentSpec.$modules = null;
1978
- currentSpec = null;
1979
-
1980
- if (injector) {
1981
- injector.get('$rootElement').off();
1982
- injector.get('$browser').pollFns.length = 0;
1983
- }
1984
-
1985
- angular.mock.clearDataCache();
1986
-
1987
- // clean up jquery's fragment cache
1988
- angular.forEach(angular.element.fragments, function(val, key) {
1989
- delete angular.element.fragments[key];
1990
- });
1991
-
1992
- MockXhr.$$lastInstance = null;
1993
-
1994
- angular.forEach(angular.callbacks, function(val, key) {
1995
- delete angular.callbacks[key];
1996
- });
1997
- angular.callbacks.counter = 0;
1998
- });
1999
-
2000
- /**
2001
- * @ngdoc function
2002
- * @name angular.mock.module
2003
- * @description
2004
- *
2005
- * *NOTE*: This function is also published on window for easy access.<br>
2006
- *
2007
- * This function registers a module configuration code. It collects the configuration information
2008
- * which will be used when the injector is created by {@link angular.mock.inject inject}.
2009
- *
2010
- * See {@link angular.mock.inject inject} for usage example
2011
- *
2012
- * @param {...(string|Function|Object)} fns any number of modules which are represented as string
2013
- * aliases or as anonymous module initialization functions. The modules are used to
2014
- * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
2015
- * object literal is passed they will be registered as values in the module, the key being
2016
- * the module name and the value being what is returned.
2017
- */
2018
- window.module = angular.mock.module = function() {
2019
- var moduleFns = Array.prototype.slice.call(arguments, 0);
2020
- return isSpecRunning() ? workFn() : workFn;
2021
- /////////////////////
2022
- function workFn() {
2023
- if (currentSpec.$injector) {
2024
- throw new Error('Injector already created, can not register a module!');
2025
- } else {
2026
- var modules = currentSpec.$modules || (currentSpec.$modules = []);
2027
- angular.forEach(moduleFns, function(module) {
2028
- if (angular.isObject(module) && !angular.isArray(module)) {
2029
- modules.push(function($provide) {
2030
- angular.forEach(module, function(value, key) {
2031
- $provide.value(key, value);
2032
- });
2033
- });
2034
- } else {
2035
- modules.push(module);
2036
- }
2037
- });
2038
- }
2039
- }
2040
- };
2041
-
2042
- /**
2043
- * @ngdoc function
2044
- * @name angular.mock.inject
2045
- * @description
2046
- *
2047
- * *NOTE*: This function is also published on window for easy access.<br>
2048
- *
2049
- * The inject function wraps a function into an injectable function. The inject() creates new
2050
- * instance of {@link auto.$injector $injector} per test, which is then used for
2051
- * resolving references.
2052
- *
2053
- *
2054
- * ## Resolving References (Underscore Wrapping)
2055
- * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
2056
- * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
2057
- * that is declared in the scope of the `describe()` block. Since we would, most likely, want
2058
- * the variable to have the same name of the reference we have a problem, since the parameter
2059
- * to the `inject()` function would hide the outer variable.
2060
- *
2061
- * To help with this, the injected parameters can, optionally, be enclosed with underscores.
2062
- * These are ignored by the injector when the reference name is resolved.
2063
- *
2064
- * For example, the parameter `_myService_` would be resolved as the reference `myService`.
2065
- * Since it is available in the function body as _myService_, we can then assign it to a variable
2066
- * defined in an outer scope.
2067
- *
2068
- * ```
2069
- * // Defined out reference variable outside
2070
- * var myService;
2071
- *
2072
- * // Wrap the parameter in underscores
2073
- * beforeEach( inject( function(_myService_){
2074
- * myService = _myService_;
2075
- * }));
2076
- *
2077
- * // Use myService in a series of tests.
2078
- * it('makes use of myService', function() {
2079
- * myService.doStuff();
2080
- * });
2081
- *
2082
- * ```
2083
- *
2084
- * See also {@link angular.mock.module angular.mock.module}
2085
- *
2086
- * ## Example
2087
- * Example of what a typical jasmine tests looks like with the inject method.
2088
- * ```js
2089
- *
2090
- * angular.module('myApplicationModule', [])
2091
- * .value('mode', 'app')
2092
- * .value('version', 'v1.0.1');
2093
- *
2094
- *
2095
- * describe('MyApp', function() {
2096
- *
2097
- * // You need to load modules that you want to test,
2098
- * // it loads only the "ng" module by default.
2099
- * beforeEach(module('myApplicationModule'));
2100
- *
2101
- *
2102
- * // inject() is used to inject arguments of all given functions
2103
- * it('should provide a version', inject(function(mode, version) {
2104
- * expect(version).toEqual('v1.0.1');
2105
- * expect(mode).toEqual('app');
2106
- * }));
2107
- *
2108
- *
2109
- * // The inject and module method can also be used inside of the it or beforeEach
2110
- * it('should override a version and test the new version is injected', function() {
2111
- * // module() takes functions or strings (module aliases)
2112
- * module(function($provide) {
2113
- * $provide.value('version', 'overridden'); // override version here
2114
- * });
2115
- *
2116
- * inject(function(version) {
2117
- * expect(version).toEqual('overridden');
2118
- * });
2119
- * });
2120
- * });
2121
- *
2122
- * ```
2123
- *
2124
- * @param {...Function} fns any number of functions which will be injected using the injector.
2125
- */
2126
-
2127
-
2128
-
2129
- var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
2130
- this.message = e.message;
2131
- this.name = e.name;
2132
- if (e.line) this.line = e.line;
2133
- if (e.sourceId) this.sourceId = e.sourceId;
2134
- if (e.stack && errorForStack)
2135
- this.stack = e.stack + '\n' + errorForStack.stack;
2136
- if (e.stackArray) this.stackArray = e.stackArray;
2137
- };
2138
- ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString;
2139
-
2140
- window.inject = angular.mock.inject = function() {
2141
- var blockFns = Array.prototype.slice.call(arguments, 0);
2142
- var errorForStack = new Error('Declaration Location');
2143
- return isSpecRunning() ? workFn.call(currentSpec) : workFn;
2144
- /////////////////////
2145
- function workFn() {
2146
- var modules = currentSpec.$modules || [];
2147
-
2148
- modules.unshift('ngMock');
2149
- modules.unshift('ng');
2150
- var injector = currentSpec.$injector;
2151
- if (!injector) {
2152
- injector = currentSpec.$injector = angular.injector(modules);
2153
- }
2154
- for(var i = 0, ii = blockFns.length; i < ii; i++) {
2155
- try {
2156
- /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
2157
- injector.invoke(blockFns[i] || angular.noop, this);
2158
- /* jshint +W040 */
2159
- } catch (e) {
2160
- if (e.stack && errorForStack) {
2161
- throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
2162
- }
2163
- throw e;
2164
- } finally {
2165
- errorForStack = null;
2166
- }
2167
- }
2168
- }
2169
- };
2170
- }
2171
-
2172
-
2173
- })(window, window.angular);