GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Code Duplication    Length = 460-460 lines in 2 locations

third-party/angularjs-modules-plugins/UI-Bootstrap/ui-bootstrap-tpls-1.3.2.js 1 location

@@ 745-1204 (lines=460) @@
742
743
angular.module('ui.bootstrap.dateparser', [])
744
745
.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', function($log, $locale, dateFilter, orderByFilter) {
746
  // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
747
  var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
748
749
  var localeId;
750
  var formatCodeToRegex;
751
752
  this.init = function() {
753
    localeId = $locale.id;
754
755
    this.parsers = {};
756
    this.formatters = {};
757
758
    formatCodeToRegex = [
759
      {
760
        key: 'yyyy',
761
        regex: '\\d{4}',
762
        apply: function(value) { this.year = +value; },
763
        formatter: function(date) {
764
          var _date = new Date();
765
          _date.setFullYear(Math.abs(date.getFullYear()));
766
          return dateFilter(_date, 'yyyy');
767
        }
768
      },
769
      {
770
        key: 'yy',
771
        regex: '\\d{2}',
772
        apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; },
773
        formatter: function(date) {
774
          var _date = new Date();
775
          _date.setFullYear(Math.abs(date.getFullYear()));
776
          return dateFilter(_date, 'yy');
777
        }
778
      },
779
      {
780
        key: 'y',
781
        regex: '\\d{1,4}',
782
        apply: function(value) { this.year = +value; },
783
        formatter: function(date) {
784
          var _date = new Date();
785
          _date.setFullYear(Math.abs(date.getFullYear()));
786
          return dateFilter(_date, 'y');
787
        }
788
      },
789
      {
790
        key: 'M!',
791
        regex: '0?[1-9]|1[0-2]',
792
        apply: function(value) { this.month = value - 1; },
793
        formatter: function(date) {
794
          var value = date.getMonth();
795
          if (/^[0-9]$/.test(value)) {
796
            return dateFilter(date, 'MM');
797
          }
798
799
          return dateFilter(date, 'M');
800
        }
801
      },
802
      {
803
        key: 'MMMM',
804
        regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
805
        apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); },
806
        formatter: function(date) { return dateFilter(date, 'MMMM'); }
807
      },
808
      {
809
        key: 'MMM',
810
        regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
811
        apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); },
812
        formatter: function(date) { return dateFilter(date, 'MMM'); }
813
      },
814
      {
815
        key: 'MM',
816
        regex: '0[1-9]|1[0-2]',
817
        apply: function(value) { this.month = value - 1; },
818
        formatter: function(date) { return dateFilter(date, 'MM'); }
819
      },
820
      {
821
        key: 'M',
822
        regex: '[1-9]|1[0-2]',
823
        apply: function(value) { this.month = value - 1; },
824
        formatter: function(date) { return dateFilter(date, 'M'); }
825
      },
826
      {
827
        key: 'd!',
828
        regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
829
        apply: function(value) { this.date = +value; },
830
        formatter: function(date) {
831
          var value = date.getDate();
832
          if (/^[1-9]$/.test(value)) {
833
            return dateFilter(date, 'dd');
834
          }
835
836
          return dateFilter(date, 'd');
837
        }
838
      },
839
      {
840
        key: 'dd',
841
        regex: '[0-2][0-9]{1}|3[0-1]{1}',
842
        apply: function(value) { this.date = +value; },
843
        formatter: function(date) { return dateFilter(date, 'dd'); }
844
      },
845
      {
846
        key: 'd',
847
        regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
848
        apply: function(value) { this.date = +value; },
849
        formatter: function(date) { return dateFilter(date, 'd'); }
850
      },
851
      {
852
        key: 'EEEE',
853
        regex: $locale.DATETIME_FORMATS.DAY.join('|'),
854
        formatter: function(date) { return dateFilter(date, 'EEEE'); }
855
      },
856
      {
857
        key: 'EEE',
858
        regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
859
        formatter: function(date) { return dateFilter(date, 'EEE'); }
860
      },
861
      {
862
        key: 'HH',
863
        regex: '(?:0|1)[0-9]|2[0-3]',
864
        apply: function(value) { this.hours = +value; },
865
        formatter: function(date) { return dateFilter(date, 'HH'); }
866
      },
867
      {
868
        key: 'hh',
869
        regex: '0[0-9]|1[0-2]',
870
        apply: function(value) { this.hours = +value; },
871
        formatter: function(date) { return dateFilter(date, 'hh'); }
872
      },
873
      {
874
        key: 'H',
875
        regex: '1?[0-9]|2[0-3]',
876
        apply: function(value) { this.hours = +value; },
877
        formatter: function(date) { return dateFilter(date, 'H'); }
878
      },
879
      {
880
        key: 'h',
881
        regex: '[0-9]|1[0-2]',
882
        apply: function(value) { this.hours = +value; },
883
        formatter: function(date) { return dateFilter(date, 'h'); }
884
      },
885
      {
886
        key: 'mm',
887
        regex: '[0-5][0-9]',
888
        apply: function(value) { this.minutes = +value; },
889
        formatter: function(date) { return dateFilter(date, 'mm'); }
890
      },
891
      {
892
        key: 'm',
893
        regex: '[0-9]|[1-5][0-9]',
894
        apply: function(value) { this.minutes = +value; },
895
        formatter: function(date) { return dateFilter(date, 'm'); }
896
      },
897
      {
898
        key: 'sss',
899
        regex: '[0-9][0-9][0-9]',
900
        apply: function(value) { this.milliseconds = +value; },
901
        formatter: function(date) { return dateFilter(date, 'sss'); }
902
      },
903
      {
904
        key: 'ss',
905
        regex: '[0-5][0-9]',
906
        apply: function(value) { this.seconds = +value; },
907
        formatter: function(date) { return dateFilter(date, 'ss'); }
908
      },
909
      {
910
        key: 's',
911
        regex: '[0-9]|[1-5][0-9]',
912
        apply: function(value) { this.seconds = +value; },
913
        formatter: function(date) { return dateFilter(date, 's'); }
914
      },
915
      {
916
        key: 'a',
917
        regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
918
        apply: function(value) {
919
          if (this.hours === 12) {
920
            this.hours = 0;
921
          }
922
923
          if (value === 'PM') {
924
            this.hours += 12;
925
          }
926
        },
927
        formatter: function(date) { return dateFilter(date, 'a'); }
928
      },
929
      {
930
        key: 'Z',
931
        regex: '[+-]\\d{4}',
932
        apply: function(value) {
933
          var matches = value.match(/([+-])(\d{2})(\d{2})/),
934
            sign = matches[1],
935
            hours = matches[2],
936
            minutes = matches[3];
937
          this.hours += toInt(sign + hours);
938
          this.minutes += toInt(sign + minutes);
939
        },
940
        formatter: function(date) {
941
          return dateFilter(date, 'Z');
942
        }
943
      },
944
      {
945
        key: 'ww',
946
        regex: '[0-4][0-9]|5[0-3]',
947
        formatter: function(date) { return dateFilter(date, 'ww'); }
948
      },
949
      {
950
        key: 'w',
951
        regex: '[0-9]|[1-4][0-9]|5[0-3]',
952
        formatter: function(date) { return dateFilter(date, 'w'); }
953
      },
954
      {
955
        key: 'GGGG',
956
        regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'),
957
        formatter: function(date) { return dateFilter(date, 'GGGG'); }
958
      },
959
      {
960
        key: 'GGG',
961
        regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
962
        formatter: function(date) { return dateFilter(date, 'GGG'); }
963
      },
964
      {
965
        key: 'GG',
966
        regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
967
        formatter: function(date) { return dateFilter(date, 'GG'); }
968
      },
969
      {
970
        key: 'G',
971
        regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
972
        formatter: function(date) { return dateFilter(date, 'G'); }
973
      }
974
    ];
975
  };
976
977
  this.init();
978
979
  function createParser(format, func) {
980
    var map = [], regex = format.split('');
981
982
    // check for literal values
983
    var quoteIndex = format.indexOf('\'');
984
    if (quoteIndex > -1) {
985
      var inLiteral = false;
986
      format = format.split('');
987
      for (var i = quoteIndex; i < format.length; i++) {
988
        if (inLiteral) {
989
          if (format[i] === '\'') {
990
            if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
991
              format[i+1] = '$';
992
              regex[i+1] = '';
993
            } else { // end of literal
994
              regex[i] = '';
995
              inLiteral = false;
996
            }
997
          }
998
          format[i] = '$';
999
        } else {
1000
          if (format[i] === '\'') { // start of literal
1001
            format[i] = '$';
1002
            regex[i] = '';
1003
            inLiteral = true;
1004
          }
1005
        }
1006
      }
1007
1008
      format = format.join('');
1009
    }
1010
1011
    angular.forEach(formatCodeToRegex, function(data) {
1012
      var index = format.indexOf(data.key);
1013
1014
      if (index > -1) {
1015
        format = format.split('');
1016
1017
        regex[index] = '(' + data.regex + ')';
1018
        format[index] = '$'; // Custom symbol to define consumed part of format
1019
        for (var i = index + 1, n = index + data.key.length; i < n; i++) {
1020
          regex[i] = '';
1021
          format[i] = '$';
1022
        }
1023
        format = format.join('');
1024
1025
        map.push({
1026
          index: index,
1027
          key: data.key,
1028
          apply: data[func],
1029
          matcher: data.regex
1030
        });
1031
      }
1032
    });
1033
1034
    return {
1035
      regex: new RegExp('^' + regex.join('') + '$'),
1036
      map: orderByFilter(map, 'index')
1037
    };
1038
  }
1039
1040
  this.filter = function(date, format) {
1041
    if (!angular.isDate(date) || isNaN(date) || !format) {
1042
      return '';
1043
    }
1044
1045
    format = $locale.DATETIME_FORMATS[format] || format;
1046
1047
    if ($locale.id !== localeId) {
1048
      this.init();
1049
    }
1050
1051
    if (!this.formatters[format]) {
1052
      this.formatters[format] = createParser(format, 'formatter');
1053
    }
1054
1055
    var parser = this.formatters[format],
1056
      map = parser.map;
1057
1058
    var _format = format;
1059
1060
    return map.reduce(function(str, mapper, i) {
1061
      var match = _format.match(new RegExp('(.*)' + mapper.key));
1062
      if (match && angular.isString(match[1])) {
1063
        str += match[1];
1064
        _format = _format.replace(match[1] + mapper.key, '');
1065
      }
1066
1067
      var endStr = i === map.length - 1 ? _format : '';
1068
1069
      if (mapper.apply) {
1070
        return str + mapper.apply.call(null, date) + endStr;
1071
      }
1072
1073
      return str + endStr;
1074
    }, '');
1075
  };
1076
1077
  this.parse = function(input, format, baseDate) {
1078
    if (!angular.isString(input) || !format) {
1079
      return input;
1080
    }
1081
1082
    format = $locale.DATETIME_FORMATS[format] || format;
1083
    format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
1084
1085
    if ($locale.id !== localeId) {
1086
      this.init();
1087
    }
1088
1089
    if (!this.parsers[format]) {
1090
      this.parsers[format] = createParser(format, 'apply');
1091
    }
1092
1093
    var parser = this.parsers[format],
1094
        regex = parser.regex,
1095
        map = parser.map,
1096
        results = input.match(regex),
1097
        tzOffset = false;
1098
    if (results && results.length) {
1099
      var fields, dt;
1100
      if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
1101
        fields = {
1102
          year: baseDate.getFullYear(),
1103
          month: baseDate.getMonth(),
1104
          date: baseDate.getDate(),
1105
          hours: baseDate.getHours(),
1106
          minutes: baseDate.getMinutes(),
1107
          seconds: baseDate.getSeconds(),
1108
          milliseconds: baseDate.getMilliseconds()
1109
        };
1110
      } else {
1111
        if (baseDate) {
1112
          $log.warn('dateparser:', 'baseDate is not a valid date');
1113
        }
1114
        fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
1115
      }
1116
1117
      for (var i = 1, n = results.length; i < n; i++) {
1118
        var mapper = map[i - 1];
1119
        if (mapper.matcher === 'Z') {
1120
          tzOffset = true;
1121
        }
1122
1123
        if (mapper.apply) {
1124
          mapper.apply.call(fields, results[i]);
1125
        }
1126
      }
1127
1128
      var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
1129
        Date.prototype.setFullYear;
1130
      var timesetter = tzOffset ? Date.prototype.setUTCHours :
1131
        Date.prototype.setHours;
1132
1133
      if (isValid(fields.year, fields.month, fields.date)) {
1134
        if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
1135
          dt = new Date(baseDate);
1136
          datesetter.call(dt, fields.year, fields.month, fields.date);
1137
          timesetter.call(dt, fields.hours, fields.minutes,
1138
            fields.seconds, fields.milliseconds);
1139
        } else {
1140
          dt = new Date(0);
1141
          datesetter.call(dt, fields.year, fields.month, fields.date);
1142
          timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
1143
            fields.seconds || 0, fields.milliseconds || 0);
1144
        }
1145
      }
1146
1147
      return dt;
1148
    }
1149
  };
1150
1151
  // Check if date is valid for specific month (and year for February).
1152
  // Month: 0 = Jan, 1 = Feb, etc
1153
  function isValid(year, month, date) {
1154
    if (date < 1) {
1155
      return false;
1156
    }
1157
1158
    if (month === 1 && date > 28) {
1159
      return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
1160
    }
1161
1162
    if (month === 3 || month === 5 || month === 8 || month === 10) {
1163
      return date < 31;
1164
    }
1165
1166
    return true;
1167
  }
1168
1169
  function toInt(str) {
1170
    return parseInt(str, 10);
1171
  }
1172
1173
  this.toTimezone = toTimezone;
1174
  this.fromTimezone = fromTimezone;
1175
  this.timezoneToOffset = timezoneToOffset;
1176
  this.addDateMinutes = addDateMinutes;
1177
  this.convertTimezoneToLocal = convertTimezoneToLocal;
1178
1179
  function toTimezone(date, timezone) {
1180
    return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
1181
  }
1182
1183
  function fromTimezone(date, timezone) {
1184
    return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
1185
  }
1186
1187
  //https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207
1188
  function timezoneToOffset(timezone, fallback) {
1189
    var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1190
    return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1191
  }
1192
1193
  function addDateMinutes(date, minutes) {
1194
    date = new Date(date.getTime());
1195
    date.setMinutes(date.getMinutes() + minutes);
1196
    return date;
1197
  }
1198
1199
  function convertTimezoneToLocal(date, timezone, reverse) {
1200
    reverse = reverse ? -1 : 1;
1201
    var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1202
    return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1203
  }
1204
}]);
1205
1206
// Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
1207
// at most one element.

third-party/angularjs-modules-plugins/UI-Bootstrap/ui-bootstrap-1.3.2.js 1 location

@@ 744-1203 (lines=460) @@
741
742
angular.module('ui.bootstrap.dateparser', [])
743
744
.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', function($log, $locale, dateFilter, orderByFilter) {
745
  // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
746
  var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
747
748
  var localeId;
749
  var formatCodeToRegex;
750
751
  this.init = function() {
752
    localeId = $locale.id;
753
754
    this.parsers = {};
755
    this.formatters = {};
756
757
    formatCodeToRegex = [
758
      {
759
        key: 'yyyy',
760
        regex: '\\d{4}',
761
        apply: function(value) { this.year = +value; },
762
        formatter: function(date) {
763
          var _date = new Date();
764
          _date.setFullYear(Math.abs(date.getFullYear()));
765
          return dateFilter(_date, 'yyyy');
766
        }
767
      },
768
      {
769
        key: 'yy',
770
        regex: '\\d{2}',
771
        apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; },
772
        formatter: function(date) {
773
          var _date = new Date();
774
          _date.setFullYear(Math.abs(date.getFullYear()));
775
          return dateFilter(_date, 'yy');
776
        }
777
      },
778
      {
779
        key: 'y',
780
        regex: '\\d{1,4}',
781
        apply: function(value) { this.year = +value; },
782
        formatter: function(date) {
783
          var _date = new Date();
784
          _date.setFullYear(Math.abs(date.getFullYear()));
785
          return dateFilter(_date, 'y');
786
        }
787
      },
788
      {
789
        key: 'M!',
790
        regex: '0?[1-9]|1[0-2]',
791
        apply: function(value) { this.month = value - 1; },
792
        formatter: function(date) {
793
          var value = date.getMonth();
794
          if (/^[0-9]$/.test(value)) {
795
            return dateFilter(date, 'MM');
796
          }
797
798
          return dateFilter(date, 'M');
799
        }
800
      },
801
      {
802
        key: 'MMMM',
803
        regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
804
        apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); },
805
        formatter: function(date) { return dateFilter(date, 'MMMM'); }
806
      },
807
      {
808
        key: 'MMM',
809
        regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
810
        apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); },
811
        formatter: function(date) { return dateFilter(date, 'MMM'); }
812
      },
813
      {
814
        key: 'MM',
815
        regex: '0[1-9]|1[0-2]',
816
        apply: function(value) { this.month = value - 1; },
817
        formatter: function(date) { return dateFilter(date, 'MM'); }
818
      },
819
      {
820
        key: 'M',
821
        regex: '[1-9]|1[0-2]',
822
        apply: function(value) { this.month = value - 1; },
823
        formatter: function(date) { return dateFilter(date, 'M'); }
824
      },
825
      {
826
        key: 'd!',
827
        regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
828
        apply: function(value) { this.date = +value; },
829
        formatter: function(date) {
830
          var value = date.getDate();
831
          if (/^[1-9]$/.test(value)) {
832
            return dateFilter(date, 'dd');
833
          }
834
835
          return dateFilter(date, 'd');
836
        }
837
      },
838
      {
839
        key: 'dd',
840
        regex: '[0-2][0-9]{1}|3[0-1]{1}',
841
        apply: function(value) { this.date = +value; },
842
        formatter: function(date) { return dateFilter(date, 'dd'); }
843
      },
844
      {
845
        key: 'd',
846
        regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
847
        apply: function(value) { this.date = +value; },
848
        formatter: function(date) { return dateFilter(date, 'd'); }
849
      },
850
      {
851
        key: 'EEEE',
852
        regex: $locale.DATETIME_FORMATS.DAY.join('|'),
853
        formatter: function(date) { return dateFilter(date, 'EEEE'); }
854
      },
855
      {
856
        key: 'EEE',
857
        regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
858
        formatter: function(date) { return dateFilter(date, 'EEE'); }
859
      },
860
      {
861
        key: 'HH',
862
        regex: '(?:0|1)[0-9]|2[0-3]',
863
        apply: function(value) { this.hours = +value; },
864
        formatter: function(date) { return dateFilter(date, 'HH'); }
865
      },
866
      {
867
        key: 'hh',
868
        regex: '0[0-9]|1[0-2]',
869
        apply: function(value) { this.hours = +value; },
870
        formatter: function(date) { return dateFilter(date, 'hh'); }
871
      },
872
      {
873
        key: 'H',
874
        regex: '1?[0-9]|2[0-3]',
875
        apply: function(value) { this.hours = +value; },
876
        formatter: function(date) { return dateFilter(date, 'H'); }
877
      },
878
      {
879
        key: 'h',
880
        regex: '[0-9]|1[0-2]',
881
        apply: function(value) { this.hours = +value; },
882
        formatter: function(date) { return dateFilter(date, 'h'); }
883
      },
884
      {
885
        key: 'mm',
886
        regex: '[0-5][0-9]',
887
        apply: function(value) { this.minutes = +value; },
888
        formatter: function(date) { return dateFilter(date, 'mm'); }
889
      },
890
      {
891
        key: 'm',
892
        regex: '[0-9]|[1-5][0-9]',
893
        apply: function(value) { this.minutes = +value; },
894
        formatter: function(date) { return dateFilter(date, 'm'); }
895
      },
896
      {
897
        key: 'sss',
898
        regex: '[0-9][0-9][0-9]',
899
        apply: function(value) { this.milliseconds = +value; },
900
        formatter: function(date) { return dateFilter(date, 'sss'); }
901
      },
902
      {
903
        key: 'ss',
904
        regex: '[0-5][0-9]',
905
        apply: function(value) { this.seconds = +value; },
906
        formatter: function(date) { return dateFilter(date, 'ss'); }
907
      },
908
      {
909
        key: 's',
910
        regex: '[0-9]|[1-5][0-9]',
911
        apply: function(value) { this.seconds = +value; },
912
        formatter: function(date) { return dateFilter(date, 's'); }
913
      },
914
      {
915
        key: 'a',
916
        regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
917
        apply: function(value) {
918
          if (this.hours === 12) {
919
            this.hours = 0;
920
          }
921
922
          if (value === 'PM') {
923
            this.hours += 12;
924
          }
925
        },
926
        formatter: function(date) { return dateFilter(date, 'a'); }
927
      },
928
      {
929
        key: 'Z',
930
        regex: '[+-]\\d{4}',
931
        apply: function(value) {
932
          var matches = value.match(/([+-])(\d{2})(\d{2})/),
933
            sign = matches[1],
934
            hours = matches[2],
935
            minutes = matches[3];
936
          this.hours += toInt(sign + hours);
937
          this.minutes += toInt(sign + minutes);
938
        },
939
        formatter: function(date) {
940
          return dateFilter(date, 'Z');
941
        }
942
      },
943
      {
944
        key: 'ww',
945
        regex: '[0-4][0-9]|5[0-3]',
946
        formatter: function(date) { return dateFilter(date, 'ww'); }
947
      },
948
      {
949
        key: 'w',
950
        regex: '[0-9]|[1-4][0-9]|5[0-3]',
951
        formatter: function(date) { return dateFilter(date, 'w'); }
952
      },
953
      {
954
        key: 'GGGG',
955
        regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'),
956
        formatter: function(date) { return dateFilter(date, 'GGGG'); }
957
      },
958
      {
959
        key: 'GGG',
960
        regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
961
        formatter: function(date) { return dateFilter(date, 'GGG'); }
962
      },
963
      {
964
        key: 'GG',
965
        regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
966
        formatter: function(date) { return dateFilter(date, 'GG'); }
967
      },
968
      {
969
        key: 'G',
970
        regex: $locale.DATETIME_FORMATS.ERAS.join('|'),
971
        formatter: function(date) { return dateFilter(date, 'G'); }
972
      }
973
    ];
974
  };
975
976
  this.init();
977
978
  function createParser(format, func) {
979
    var map = [], regex = format.split('');
980
981
    // check for literal values
982
    var quoteIndex = format.indexOf('\'');
983
    if (quoteIndex > -1) {
984
      var inLiteral = false;
985
      format = format.split('');
986
      for (var i = quoteIndex; i < format.length; i++) {
987
        if (inLiteral) {
988
          if (format[i] === '\'') {
989
            if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
990
              format[i+1] = '$';
991
              regex[i+1] = '';
992
            } else { // end of literal
993
              regex[i] = '';
994
              inLiteral = false;
995
            }
996
          }
997
          format[i] = '$';
998
        } else {
999
          if (format[i] === '\'') { // start of literal
1000
            format[i] = '$';
1001
            regex[i] = '';
1002
            inLiteral = true;
1003
          }
1004
        }
1005
      }
1006
1007
      format = format.join('');
1008
    }
1009
1010
    angular.forEach(formatCodeToRegex, function(data) {
1011
      var index = format.indexOf(data.key);
1012
1013
      if (index > -1) {
1014
        format = format.split('');
1015
1016
        regex[index] = '(' + data.regex + ')';
1017
        format[index] = '$'; // Custom symbol to define consumed part of format
1018
        for (var i = index + 1, n = index + data.key.length; i < n; i++) {
1019
          regex[i] = '';
1020
          format[i] = '$';
1021
        }
1022
        format = format.join('');
1023
1024
        map.push({
1025
          index: index,
1026
          key: data.key,
1027
          apply: data[func],
1028
          matcher: data.regex
1029
        });
1030
      }
1031
    });
1032
1033
    return {
1034
      regex: new RegExp('^' + regex.join('') + '$'),
1035
      map: orderByFilter(map, 'index')
1036
    };
1037
  }
1038
1039
  this.filter = function(date, format) {
1040
    if (!angular.isDate(date) || isNaN(date) || !format) {
1041
      return '';
1042
    }
1043
1044
    format = $locale.DATETIME_FORMATS[format] || format;
1045
1046
    if ($locale.id !== localeId) {
1047
      this.init();
1048
    }
1049
1050
    if (!this.formatters[format]) {
1051
      this.formatters[format] = createParser(format, 'formatter');
1052
    }
1053
1054
    var parser = this.formatters[format],
1055
      map = parser.map;
1056
1057
    var _format = format;
1058
1059
    return map.reduce(function(str, mapper, i) {
1060
      var match = _format.match(new RegExp('(.*)' + mapper.key));
1061
      if (match && angular.isString(match[1])) {
1062
        str += match[1];
1063
        _format = _format.replace(match[1] + mapper.key, '');
1064
      }
1065
1066
      var endStr = i === map.length - 1 ? _format : '';
1067
1068
      if (mapper.apply) {
1069
        return str + mapper.apply.call(null, date) + endStr;
1070
      }
1071
1072
      return str + endStr;
1073
    }, '');
1074
  };
1075
1076
  this.parse = function(input, format, baseDate) {
1077
    if (!angular.isString(input) || !format) {
1078
      return input;
1079
    }
1080
1081
    format = $locale.DATETIME_FORMATS[format] || format;
1082
    format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
1083
1084
    if ($locale.id !== localeId) {
1085
      this.init();
1086
    }
1087
1088
    if (!this.parsers[format]) {
1089
      this.parsers[format] = createParser(format, 'apply');
1090
    }
1091
1092
    var parser = this.parsers[format],
1093
        regex = parser.regex,
1094
        map = parser.map,
1095
        results = input.match(regex),
1096
        tzOffset = false;
1097
    if (results && results.length) {
1098
      var fields, dt;
1099
      if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
1100
        fields = {
1101
          year: baseDate.getFullYear(),
1102
          month: baseDate.getMonth(),
1103
          date: baseDate.getDate(),
1104
          hours: baseDate.getHours(),
1105
          minutes: baseDate.getMinutes(),
1106
          seconds: baseDate.getSeconds(),
1107
          milliseconds: baseDate.getMilliseconds()
1108
        };
1109
      } else {
1110
        if (baseDate) {
1111
          $log.warn('dateparser:', 'baseDate is not a valid date');
1112
        }
1113
        fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
1114
      }
1115
1116
      for (var i = 1, n = results.length; i < n; i++) {
1117
        var mapper = map[i - 1];
1118
        if (mapper.matcher === 'Z') {
1119
          tzOffset = true;
1120
        }
1121
1122
        if (mapper.apply) {
1123
          mapper.apply.call(fields, results[i]);
1124
        }
1125
      }
1126
1127
      var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
1128
        Date.prototype.setFullYear;
1129
      var timesetter = tzOffset ? Date.prototype.setUTCHours :
1130
        Date.prototype.setHours;
1131
1132
      if (isValid(fields.year, fields.month, fields.date)) {
1133
        if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
1134
          dt = new Date(baseDate);
1135
          datesetter.call(dt, fields.year, fields.month, fields.date);
1136
          timesetter.call(dt, fields.hours, fields.minutes,
1137
            fields.seconds, fields.milliseconds);
1138
        } else {
1139
          dt = new Date(0);
1140
          datesetter.call(dt, fields.year, fields.month, fields.date);
1141
          timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
1142
            fields.seconds || 0, fields.milliseconds || 0);
1143
        }
1144
      }
1145
1146
      return dt;
1147
    }
1148
  };
1149
1150
  // Check if date is valid for specific month (and year for February).
1151
  // Month: 0 = Jan, 1 = Feb, etc
1152
  function isValid(year, month, date) {
1153
    if (date < 1) {
1154
      return false;
1155
    }
1156
1157
    if (month === 1 && date > 28) {
1158
      return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
1159
    }
1160
1161
    if (month === 3 || month === 5 || month === 8 || month === 10) {
1162
      return date < 31;
1163
    }
1164
1165
    return true;
1166
  }
1167
1168
  function toInt(str) {
1169
    return parseInt(str, 10);
1170
  }
1171
1172
  this.toTimezone = toTimezone;
1173
  this.fromTimezone = fromTimezone;
1174
  this.timezoneToOffset = timezoneToOffset;
1175
  this.addDateMinutes = addDateMinutes;
1176
  this.convertTimezoneToLocal = convertTimezoneToLocal;
1177
1178
  function toTimezone(date, timezone) {
1179
    return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
1180
  }
1181
1182
  function fromTimezone(date, timezone) {
1183
    return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
1184
  }
1185
1186
  //https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207
1187
  function timezoneToOffset(timezone, fallback) {
1188
    var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1189
    return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1190
  }
1191
1192
  function addDateMinutes(date, minutes) {
1193
    date = new Date(date.getTime());
1194
    date.setMinutes(date.getMinutes() + minutes);
1195
    return date;
1196
  }
1197
1198
  function convertTimezoneToLocal(date, timezone, reverse) {
1199
    reverse = reverse ? -1 : 1;
1200
    var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1201
    return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1202
  }
1203
}]);
1204
1205
// Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
1206
// at most one element.