diff --git a/ajax/libs/angular-strap/0.7.1/angular-strap.js b/ajax/libs/angular-strap/0.7.1/angular-strap.js new file mode 100644 index 000000000..0925af2c2 --- /dev/null +++ b/ajax/libs/angular-strap/0.7.1/angular-strap.js @@ -0,0 +1,1106 @@ +/** + * AngularStrap - Twitter Bootstrap directives for AngularJS + * @version v0.7.1 - 2013-03-21 + * @link http://mgcrea.github.com/angular-strap + * @author Olivier Louvignes + * @license MIT License, http://www.opensource.org/licenses/MIT + */ + +angular.module('$strap.config', []).value('$strap.config', {}); +angular.module('$strap.filters', ['$strap.config']); +angular.module('$strap.directives', ['$strap.config']); +angular.module('$strap', ['$strap.filters', '$strap.directives', '$strap.config']); + + +angular.module('$strap.directives') + +.directive('bsAlert', ['$parse', '$timeout', '$compile', function($parse, $timeout, $compile) { + 'use strict'; + + return { + restrict: 'A', + link: function postLink(scope, element, attrs) { + + var getter = $parse(attrs.bsAlert), + setter = getter.assign, + value = getter(scope); + + // For static alerts + if(!attrs.bsAlert) { + + // Setup close button + if(angular.isUndefined(attrs.closeButton) || (attrs.closeButton !== '0' && attrs.closeButton !== 'false')) { + element.prepend(''); + } + + } else { + + scope.$watch(attrs.bsAlert, function(newValue, oldValue) { + value = newValue; + + // Set alert content + element.html((newValue.title ? '' + newValue.title + ' ' : '') + newValue.content || ''); + + if(!!newValue.closed) { + element.hide(); + } + + // Compile alert content + //$timeout(function(){ + $compile(element.contents())(scope); + //}); + + // Add proper class + if(newValue.type || oldValue.type) { + oldValue.type && element.removeClass('alert-' + oldValue.type); + newValue.type && element.addClass('alert-' + newValue.type); + } + + // Setup close button + if(angular.isUndefined(attrs.closeButton) || (attrs.closeButton !== '0' && attrs.closeButton !== 'false')) { + element.prepend(''); + } + + }, true); + + } + + element.addClass('alert').alert(); + + // Support fade-in effect + if(element.hasClass('fade')) { + element.removeClass('in'); + setTimeout(function() { + element.addClass('in'); + }); + } + + var parentArray = attrs.ngRepeat && attrs.ngRepeat.split(' in ').pop(); + + element.on('close', function(ev) { + var removeElement; + + if(parentArray) { // ngRepeat, remove from parent array + ev.preventDefault(); + + element.removeClass('in'); + + removeElement = function() { + element.trigger('closed'); + if(scope.$parent) { + scope.$parent.$apply(function() { + var path = parentArray.split('.'); + var curr = scope.$parent; + + for (var i = 0; i < path.length; ++i) { + if (curr) { + curr = curr[path[i]]; + } + } + + if (curr) { + curr.splice(scope.$index, 1); + } + }); + } + }; + + $.support.transition && element.hasClass('fade') ? + element.on($.support.transition.end, removeElement) : + removeElement(); + + } else if(value) { // object, set closed property to 'true' + ev.preventDefault(); + + element.removeClass('in'); + + removeElement = function() { + element.trigger('closed'); + scope.$apply(function() { + value.closed = true; + }); + }; + + $.support.transition && element.hasClass('fade') ? + element.on($.support.transition.end, removeElement) : + removeElement(); + + } else { // static, regular behavior + } + + }); + + } + }; +}]); + + +angular.module('$strap.directives') + +.directive('bsButton', ['$parse', '$timeout', function($parse, $timeout) { + 'use strict'; + + return { + restrict: 'A', + require: '?ngModel', + link: function postLink(scope, element, attrs, controller) { + + // If we have a controller (i.e. ngModelController) then wire it up + if(controller) { + + // Set as single toggler if not part of a btn-group + if(!element.parent('[data-toggle="buttons-checkbox"], [data-toggle="buttons-radio"]').length) { + element.attr('data-toggle', 'button'); + } + + // Handle start state + var startValue = !!scope.$eval(attrs.ngModel); + if(startValue) { + element.addClass('active'); + } + + // Watch model for changes + scope.$watch(attrs.ngModel, function(newValue, oldValue) { + var bNew = !!newValue, bOld = !!oldValue; + if(bNew !== bOld) { + $.fn.button.Constructor.prototype.toggle.call(button); + // Handle $q promises + } else if(bNew && !startValue) { + element.addClass('active'); + } + }); + + } + + // Support buttons without .btn class + if(!element.hasClass('btn')) { + element.on('click.button.data-api', function (e) { + element.button('toggle'); + }); + } + + // Initialize button + element.button(); + + // Bootstrap override to handle toggling + var button = element.data('button'); + button.toggle = function() { + + if(!controller) { + return $.fn.button.Constructor.prototype.toggle.call(this); + } + + var $parent = element.parent('[data-toggle="buttons-radio"]'); + + if($parent.length) { + element.siblings('[ng-model]').each(function(k, v) { + $parse($(v).attr('ng-model')).assign(scope, false); + }); + scope.$digest(); + if(!controller.$modelValue) { + controller.$setViewValue(!controller.$modelValue); + scope.$digest(); + } + } else { + scope.$apply(function () { + controller.$setViewValue(!controller.$modelValue); + }); + } + + }; + + /*Provide scope display functions + scope._button = function(event) { + element.button(event); + }; + scope.loading = function() { + element.tooltip('loading'); + }; + scope.reset = function() { + element.tooltip('reset'); + }; + + if(attrs.loadingText) element.click(function () { + //var btn = $(this) + element.button('loading') + setTimeout(function () { + element.button('reset') + }, 1000) + });*/ + + } + }; + +}]) + +.directive('bsButtonsCheckbox', ['$parse', function($parse) { + 'use strict'; + return { + restrict: 'A', + require: '?ngModel', + compile: function compile(tElement, tAttrs, transclude) { + tElement.attr('data-toggle', 'buttons-checkbox').find('a, button').each(function(k, v) { + $(v).attr('bs-button', ''); + }); + } + }; + +}]) + +.directive('bsButtonsRadio', ['$parse', function($parse) { + 'use strict'; + return { + restrict: 'A', + require: '?ngModel', + compile: function compile(tElement, tAttrs, transclude) { + + tElement.attr('data-toggle', 'buttons-radio'); + + // Delegate to children ngModel + if(!tAttrs.ngModel) { + tElement.find('a, button').each(function(k, v) { + $(v).attr('bs-button', ''); + }); + } + + return function postLink(scope, iElement, iAttrs, controller) { + + // If we have a controller (i.e. ngModelController) then wire it up + if(controller) { + + iElement + .find('[value]').button() + .filter('[value="' + scope.$eval(iAttrs.ngModel) + '"]') + .addClass('active'); + + iElement.on('click.button.data-api', function (ev) { + scope.$apply(function () { + controller.$setViewValue($(ev.target).closest('button').attr('value')); + }); + }); + + // Watch model for changes + scope.$watch(iAttrs.ngModel, function(newValue, oldValue) { + if(newValue !== oldValue) { + var $btn = iElement.find('[value="' + scope.$eval(iAttrs.ngModel) + '"]'); + if($btn.length) { + $.fn.button.Constructor.prototype.toggle.call($btn.data('button')); + } + } + }); + + } + + }; + } + }; + +}]); + + +angular.module('$strap.directives') + +.directive('bsButtonSelect', ['$parse', '$timeout', function($parse, $timeout) { + 'use strict'; + + return { + restrict: 'A', + require: '?ngModel', + link: function postLink(scope, element, attrs, ctrl) { + + var getter = $parse(attrs.bsButtonSelect), + setter = getter.assign; + + // Bind ngModelController + if(ctrl) { + element.text(scope.$eval(attrs.ngModel)); + // Watch model for changes + scope.$watch(attrs.ngModel, function(newValue, oldValue) { + element.text(newValue); + }); + } + + // Click handling + var values, value, index, newValue; + element.bind('click', function(ev) { + values = getter(scope); + value = ctrl ? scope.$eval(attrs.ngModel) : element.text(); + index = values.indexOf(value); + newValue = index > values.length - 2 ? values[0] : values[index + 1]; + console.warn(values, newValue); + + scope.$apply(function() { + element.text(newValue); + if(ctrl) { + ctrl.$setViewValue(newValue); + } + }); + }); + } + }; + +}]); + +// https://github.com/eternicode/bootstrap-datepicker + +angular.module('$strap.directives') + +.directive('bsDatepicker', ['$timeout', '$strap.config', function($timeout, config) { + 'use strict'; + + var isTouch = 'ontouchstart' in window && !window.navigator.userAgent.match(/PhantomJS/i); + + var regexpMap = function regexpMap(language) { + language = language || 'en'; + return { + '/' : '[\\/]', + '-' : '[-]', + '.' : '[.]', + ' ' : '[\\s]', + 'dd' : '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))', + 'd' : '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))', + 'mm' : '(?:[0]?[1-9]|[1][012])', + 'm' : '(?:[0]?[1-9]|[1][012])', + 'DD' : '(?:' + $.fn.datepicker.dates[language].days.join('|') + ')', + 'D' : '(?:' + $.fn.datepicker.dates[language].daysShort.join('|') + ')', + 'MM' : '(?:' + $.fn.datepicker.dates[language].months.join('|') + ')', + 'M' : '(?:' + $.fn.datepicker.dates[language].monthsShort.join('|') + ')', + 'yyyy' : '(?:(?:[1]{1}[0-9]{1}[0-9]{1}[0-9]{1})|(?:[2]{1}[0-9]{3}))(?![[0-9]])', + 'yy' : '(?:(?:[0-9]{1}[0-9]{1}))(?![[0-9]])' + }; + }; + + var regexpForDateFormat = function regexpForDateFormat(format, language) { + var re = format, map = regexpMap(language), i; + // Abstract replaces to avoid collisions + i = 0; angular.forEach(map, function(v, k) { + re = re.split(k).join('${' + i + '}'); i++; + }); + // Replace abstracted values + i = 0; angular.forEach(map, function(v, k) { + re = re.split('${' + i + '}').join(v); i++; + }); + return new RegExp('^' + re + '$', ['i']); + }; + + return { + restrict: 'A', + require: '?ngModel', + link: function postLink(scope, element, attrs, controller) { + + var options = config.datepicker || {}, + language = attrs.language || options.language || 'en', + format = attrs.dateFormat || options.format || ($.fn.datepicker.dates[language] && $.fn.datepicker.dates[language].format) || 'mm/dd/yyyy'; + + var dateFormatRegexp = isTouch ? 'yyyy/mm/dd' : regexpForDateFormat(format, language); + + // Handle date validity according to dateFormat + if(controller) { + controller.$parsers.unshift(function(viewValue) { + //console.warn('viewValue', viewValue, dateFormatRegexp, dateFormatRegexp.test(viewValue)); + if (!viewValue || dateFormatRegexp.test(viewValue)) { + controller.$setValidity('date', true); + return viewValue; + } else { + controller.$setValidity('date', false); + return undefined; + } + }); + } + + // Use native interface for touch devices + if(isTouch && element.prop('type') === 'text') { + + element.prop('type', 'date'); + element.on('change', function(ev) { + scope.$apply(function () { + controller.$setViewValue(element.val()); + }); + }); + + } else { + + // If we have a controller (i.e. ngModelController) then wire it up + if(controller) { + element.on('changeDate', function(ev) { + scope.$apply(function () { + controller.$setViewValue(element.val()); + }); + }); + } + + // Popover GarbageCollection + var $popover = element.closest('.popover'); + if($popover) { + $popover.on('hide', function(e) { + var datepicker = element.data('datepicker'); + if(datepicker) { + datepicker.picker.remove(); + element.data('datepicker', null); + } + }); + } + + // Create datepicker + element.attr('data-toggle', 'datepicker'); + element.datepicker({ + autoclose: true, + format: format, + language: language, + forceParse: attrs.forceParse || false + }); + + } + + // Support add-on + var component = element.siblings('[data-toggle="datepicker"]'); + if(component.length) { + component.on('click', function() { element.trigger('focus'); }); + } + + } + + }; + +}]); + +// https://github.com/eternicode/bootstrap-datepicker + +angular.module('$strap.directives') + +.directive('bsDaterangepicker', ['$timeout', function($timeout) { + 'use strict'; + + var isTouch = 'ontouchstart' in window && !window.navigator.userAgent.match(/PhantomJS/i); + + // var DATE_REGEXP_MAP = { + // '/' : '[\\/]', + // '-' : '[-]', + // '.' : '[.]', + // 'dd' : '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))', + // 'd' : '(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))', + // 'mm' : '(?:[0]?[1-9]|[1][012])', + // 'm' : '(?:[0]?[1-9]|[1][012])', + // 'yyyy' : '(?:(?:[1]{1}[0-9]{1}[0-9]{1}[0-9]{1})|(?:[2]{1}[0-9]{3}))(?![[0-9]])', + // 'yy' : '(?:(?:[0-9]{1}[0-9]{1}))(?![[0-9]])' + // }; + + return { + restrict: 'A', + require: '?ngModel', + link: function postLink(scope, element, attrs, controller) { + console.log('postLink', this, arguments); window.element = element; + + // var regexpForDateFormat = function(dateFormat, options) { + // options || (options = {}); + // var re = dateFormat, regexpMap = DATE_REGEXP_MAP; + // /*if(options.mask) { + // regexpMap['/'] = ''; + // regexpMap['-'] = ''; + // }*/ + // angular.forEach(regexpMap, function(v, k) { re = re.split(k).join(v); }); + // return new RegExp('^' + re + '$', ['i']); + // }; + + // var dateFormatRegexp = isTouch ? 'yyyy/mm/dd' : regexpForDateFormat(attrs.dateFormat || 'mm/dd/yyyy'/*, {mask: !!attrs.uiMask}*/); + + // // Handle date validity according to dateFormat + // if(controller) { + // controller.$parsers.unshift(function(viewValue) { + // //console.warn('viewValue', viewValue, dateFormatRegexp, dateFormatRegexp.test(viewValue)); + // if (!viewValue || dateFormatRegexp.test(viewValue)) { + // controller.$setValidity('date', true); + // return viewValue; + // } else { + // controller.$setValidity('date', false); + // return undefined; + // } + // }); + // } + + // // Support add-on + // var component = element.next('[data-toggle="datepicker"]'); + // if(component.length) { + // component.on('click', function() { isTouch ? element.trigger('focus') : element.datepicker('show'); }); + // } + + // // Use native interface for touch devices + // if(isTouch && element.prop('type') === 'text') { + + // element.prop('type', 'date'); + // element.on('change', function(ev) { + // scope.$apply(function () { + // controller.$setViewValue(element.val()); + // }); + // }); + + // } else { + + // // If we have a controller (i.e. ngModelController) then wire it up + // if(controller) { + // element.on('changeDate', function(ev) { + // scope.$apply(function () { + // controller.$setViewValue(element.val()); + // }); + // }); + // } + + // // Popover GarbageCollection + // var $popover = element.closest('.popover'); + // if($popover) { + // $popover.on('hide', function(e) { + // var datepicker = element.data('datepicker'); + // if(datepicker) { + // datepicker.picker.remove(); + // element.data('datepicker', null); + // } + // }); + // } + + // Create daterangepicker + element.attr('data-toggle', 'daterangepicker'); + element.daterangepicker({ + // autoclose: true, + // forceParse: attrs.forceParse || false, + // language: attrs.language || 'en' + }); + + // } + + } + + }; + +}]); + + +angular.module('$strap.directives') + +.directive('bsDropdown', ['$parse', '$compile', '$timeout', function($parse, $compile, $timeout) { + 'use strict'; + + var buildTemplate = function(items, ul) { + if(!ul) ul = ['']; + angular.forEach(items, function(item, index) { + if(item.divider) return ul.splice(index + 1, 0, '
  • '); + var li = '' + + '' + + (item.text || '') + ''; + if(item.submenu && item.submenu.length) li += buildTemplate(item.submenu).join("\n"); + li += ''; + ul.splice(index + 1, 0, li); + }); + return ul; + }; + + return { + restrict: 'EA', + scope: true, + link: function postLink(scope, iElement, iAttrs) { + + var getter = $parse(iAttrs.bsDropdown), + items = getter(scope); + + // Defer after any ngRepeat rendering + $timeout(function() { + + if(!angular.isArray(items)) { + // @todo? + } + + var dropdown = angular.element(buildTemplate(items).join('')); + dropdown.insertAfter(iElement); + + // Compile dropdown-menu + $compile(iElement.next('ul.dropdown-menu'))(scope); + + }); + + iElement + .addClass('dropdown-toggle') + .attr('data-toggle', "dropdown"); + + } + }; + +}]); + +angular.module('$strap.directives') + +.directive('bsModal', ['$parse', '$compile', '$http', '$timeout', '$q', '$templateCache', function($parse, $compile, $http, $timeout, $q, $templateCache) { + 'use strict'; + + return { + restrict: 'A', + scope: true, + link: function postLink(scope, element, attr, ctrl) { + + var getter = $parse(attr.bsModal), + setter = getter.assign, + value = getter(scope); + + $q.when($templateCache.get(value) || $http.get(value, {cache: true})).then(function onSuccess(template) { + + // Handle response from $http promise + if(angular.isObject(template)) { + template = template.data; + } + + // Build modal object + var id = getter(scope).replace('.html', '').replace(/[\/|\.|:]/g, "-") + '-' + scope.$id; + var $modal = $('') + .attr('id', id) + .attr('data-backdrop', attr.backdrop || true) + .attr('data-keyboard', attr.keyboard || true) + .addClass(attr.modalClass ? 'fade ' + attr.modalClass : 'fade') + .html(template); + + $('body').append($modal); + + // Configure element + element.attr('href', '#' + id).attr('data-toggle', 'modal'); + + // Compile modal content + $timeout(function(){ + $compile($modal)(scope); + }); + + // Provide scope display functions + scope._modal = function(name) { + $modal.modal(name); + }; + scope.hide = function() { + $modal.modal('hide'); + }; + scope.show = function() { + $modal.modal('show'); + }; + scope.dismiss = scope.hide; + + $modal.on("show", function(event) { + scope.$emit("modal-show", event); + }); + + $modal.on("shown", function(event) { + scope.$emit("modal-shown", event); + }); + + $modal.on("hide", function(event) { + scope.$emit("modal-hide", event); + }); + + $modal.on("hidden", function(event) { + scope.$emit("modal-hidden", event); + }); + + }); + } + }; +}]); + + +angular.module('$strap.directives') + +.directive('bsNavbar', ['$location', function($location) { + 'use strict'; + + return { + restrict: 'A', + link: function postLink($scope, element, attrs, controller) { + // Watch for the $location + $scope.$watch(function() { + return $location.path(); + }, function(newValue, oldValue) { + + element.find('li[data-match-route]').each(function(k, li) { + var $li = angular.element(li), + // data('match-rout') does not work with dynamic attributes + pattern = $li.attr('data-match-route'), + regexp = new RegExp('^' + pattern + '$', ["i"]); + + if(regexp.test(newValue)) { + $li.addClass('active'); + } else { + $li.removeClass('active'); + } + + }); + }); + } + }; +}]); + + +angular.module('$strap.directives') + +.directive('bsPopover', ['$parse', '$compile', '$http', '$timeout', '$q', '$templateCache', function($parse, $compile, $http, $timeout, $q, $templateCache) { + 'use strict'; + + // Hide popovers when pressing esc + $("body").on("keyup", function(ev) { + if(ev.keyCode === 27) { + $(".popover.in").each(function() { + $(this).popover('hide'); + }); + } + }); + + return { + restrict: 'A', + scope: true, + link: function postLink(scope, element, attr, ctrl) { + + var getter = $parse(attr.bsPopover), + setter = getter.assign, + value = getter(scope), + options = {}; + + if(angular.isObject(value)) { + options = value; + } + + $q.when(options.content || $templateCache.get(value) || $http.get(value, {cache: true})).then(function onSuccess(template) { + + // Handle response from $http promise + if(angular.isObject(template)) { + template = template.data; + } + + // Handle data-unique attribute + if(!!attr.unique) { + element.on('show', function(ev) { // requires bootstrap 2.3.0+ + // Hide any active popover except self + $(".popover.in").each(function() { + var $this = $(this), + popover = $this.data('popover'); + if(popover && !popover.$element.is(element)) { + $this.popover('hide'); + } + }); + }); + } + + // Handle data-hide attribute to toggle visibility + if(!!attr.hide) { + scope.$watch(attr.hide, function(newValue, oldValue) { + if(!!newValue) { + popover.hide(); + } else if(newValue !== oldValue) { + popover.show(); + } + }); + } + + // Initialize popover + element.popover(angular.extend({}, options, { + content: template, + html: true + })); + + // Bootstrap override to provide tip() reference & compilation + var popover = element.data('popover'); + popover.hasContent = function() { + return this.getTitle() || template; // fix multiple $compile() + }; + popover.getPosition = function() { + var r = $.fn.popover.Constructor.prototype.getPosition.apply(this, arguments); + + // Compile content + $compile(this.$tip)(scope); + scope.$digest(); + + // Bind popover to the tip() + this.$tip.data('popover', this); + + return r; + }; + + // Provide scope display functions + scope._popover = function(name) { + element.popover(name); + }; + scope.hide = function() { + element.popover('hide'); + }; + scope.show = function() { + element.popover('show'); + }; + scope.dismiss = scope.hide; + + }); + + } + }; + +}]); + +angular.module('$strap.directives') + +.directive('bsTabs', ['$parse', '$compile', '$timeout', function($parse, $compile, $timeout) { + 'use strict'; + + return { + restrict: 'A', + require: '?ngModel', + scope: true, + link: function postLink(scope, iElement, iAttrs, controller) { + + var getter = $parse(iAttrs.bsTabs), + setter = getter.assign, + value = getter(scope); + + var tabs = ['']; + var panes = ['
    ', '
    ']; + + iElement.hide(); + var activeTab = 0; + + // Defer after any ngRepeat rendering + $timeout(function() { + + if(!angular.isArray(value)) { + + value = []; + // Convert existing dom elements + iElement.children('[data-title], [data-tab]').each(function(index) { + var $this = angular.element(this); + value.push({ + title: scope.$eval($this.data('title') || $this.data('tab')), + content: this.innerHTML, + active: $this.hasClass('active'), + fade: $this.hasClass('fade') + }); + }); + + } + + // Select correct starting activeTab + angular.forEach(value, function(tab, index) { + if(tab.active) { + activeTab = index; + } + }); + + // Build from object + angular.forEach(value, function(tab, index) { + var id = 'tab-' + scope.$id + '-' + index, + active = activeTab === index, + fade = iAttrs.fade || tab.fade; + tabs.splice(index + 1, 0, '' + tab.title + ''); + panes.splice(index + 1, 0, '
    ' + tab.content + '
    '); + }); + + iElement.html(tabs.join('') + panes.join('')).show(); + + // Compile tab-content + $compile(iElement.children('div.tab-content'))(scope); + + }); + + // If we have a controller (i.e. ngModelController) then wire it up + if(controller) { + + iElement.on('show', function(ev) { + var $target = $(ev.target); + scope.$apply(function() { + controller.$setViewValue($target.data('index')); + }); + }); + + // Watch ngModel for changes + scope.$watch(iAttrs.ngModel, function(newValue, oldValue) { + if(angular.isUndefined(newValue)) { return; } + activeTab = newValue; // update starting activeTab before first build + setTimeout(function() { + var $next = iElement.children('ul.nav-tabs').find('li:eq(' + newValue*1 + ')'); + if(!$next.hasClass('active')) { + $next.children('a').tab('show'); + } + }); + }); + + } + + } + + }; + +}]); + + +angular.module('$strap.directives') + +.directive('bsTimepicker', ['$timeout', function($timeout) { + 'use strict'; + + var TIME_REGEXP = '((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)'; + + return { + restrict: 'A', + require: '?ngModel', + link: function postLink(scope, element, attrs, controller) { + + // If we have a controller (i.e. ngModelController) then wire it up + if(controller) { + element.on('changeTime.timepicker', function(ev) { + $timeout(function() { + controller.$setViewValue(element.val()); + }); + }); + } + + // Handle input time validity + var timeRegExp = new RegExp('^' + TIME_REGEXP + '$', ['i']); + controller.$parsers.unshift(function(viewValue) { + // console.warn('viewValue', viewValue, timeRegExp, timeRegExp.test(viewValue)); + if (!viewValue || timeRegExp.test(viewValue)) { + controller.$setValidity('time', true); + return viewValue; + } else { + controller.$setValidity('time', false); + return; + } + }); + + // Create datepicker + element.attr('data-toggle', 'timepicker'); + element.parent().addClass('bootstrap-timepicker'); + //$timeout(function () { + element.timepicker(); + //}); + + } + }; + +}]); + + +angular.module('$strap.directives') + +.directive('bsTooltip', ['$parse', '$compile', function($parse, $compile) { + 'use strict'; + + return { + restrict: 'A', + scope: true, + link: function postLink(scope, element, attrs, ctrl) { + + var getter = $parse(attrs.bsTooltip), + setter = getter.assign, + value = getter(scope); + + // Watch bsTooltip for changes + scope.$watch(attrs.bsTooltip, function(newValue, oldValue) { + if(newValue !== oldValue) { + value = newValue; + } + }); + + if(!!attrs.unique) { + element.on('show', function(ev) { + // Hide any active popover except self + $(".tooltip.in").each(function() { + var $this = $(this), + tooltip = $this.data('tooltip'); + if(tooltip && !tooltip.$element.is(element)) { + $this.tooltip('hide'); + } + }); + }); + } + + // Initialize tooltip + element.tooltip({ + title: function() { return angular.isFunction(value) ? value.apply(null, arguments) : value; }, + html: true + }); + + // Bootstrap override to provide events & tip() reference + var tooltip = element.data('tooltip'); + tooltip.show = function() { + var r = $.fn.tooltip.Constructor.prototype.show.apply(this, arguments); + // Bind tooltip to the tip() + this.tip().data('tooltip', this); + return r; + }; + + //Provide scope display functions + scope._tooltip = function(event) { + element.tooltip(event); + }; + scope.hide = function() { + element.tooltip('hide'); + }; + scope.show = function() { + element.tooltip('show'); + }; + scope.dismiss = scope.hide; + + } + }; + +}]); + +angular.module('$strap.directives') + +.directive('bsTypeahead', ['$parse', function($parse) { + 'use strict'; + + return { + restrict: 'A', + require: '?ngModel', + link: function postLink(scope, element, attrs, controller) { + + var getter = $parse(attrs.bsTypeahead), + setter = getter.assign, + value = getter(scope); + + // Watch bsTypeahead for changes + scope.$watch(attrs.bsTypeahead, function(newValue, oldValue) { + if(newValue !== oldValue) { + value = newValue; + } + }); + + element.attr('data-provide', 'typeahead'); + element.typeahead({ + source: function(query) { return angular.isFunction(value) ? value.apply(null, arguments) : value; }, + minLength: attrs.minLength || 1, + items: attrs.items, + updater: function(value) { + // If we have a controller (i.e. ngModelController) then wire it up + if(controller) { + scope.$apply(function () { + controller.$setViewValue(value); + }); + } + return value; + } + }); + + // Bootstrap override + var typeahead = element.data('typeahead'); + // Fixes #2043: allows minLength of zero to enable show all for typeahead + typeahead.lookup = function (ev) { + var items; + this.query = this.$element.val() || ''; + if (this.query.length < this.options.minLength) { + return this.shown ? this.hide() : this; + } + items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source; + return items ? this.process(items) : this; + }; + + // Support 0-minLength + if(attrs.minLength === "0") { + setTimeout(function() { // Push to the event loop to make sure element.typeahead is defined (breaks tests otherwise) + element.on('focus', function() { + element.val().length === 0 && setTimeout(element.typeahead.bind(element, 'lookup'), 200); + }); + }); + } + + } + }; + +}]); diff --git a/ajax/libs/angular-strap/0.7.1/angular-strap.min.js b/ajax/libs/angular-strap/0.7.1/angular-strap.min.js new file mode 100644 index 000000000..697f7180b --- /dev/null +++ b/ajax/libs/angular-strap/0.7.1/angular-strap.min.js @@ -0,0 +1,8 @@ +/** + * AngularStrap - Twitter Bootstrap directives for AngularJS + * @version v0.7.1 - 2013-03-21 + * @link http://mgcrea.github.com/angular-strap + * @author Olivier Louvignes + * @license MIT License, http://www.opensource.org/licenses/MIT + */ +angular.module("$strap.config",[]).value("$strap.config",{}),angular.module("$strap.filters",["$strap.config"]),angular.module("$strap.directives",["$strap.config"]),angular.module("$strap",["$strap.filters","$strap.directives","$strap.config"]),angular.module("$strap.directives").directive("bsAlert",["$parse","$timeout","$compile",function(t,e,n){"use strict";return{restrict:"A",link:function(e,a,i){var o=t(i.bsAlert),r=(o.assign,o(e));i.bsAlert?e.$watch(i.bsAlert,function(t,o){r=t,a.html((t.title?""+t.title+" ":"")+t.content||""),t.closed&&a.hide(),n(a.contents())(e),(t.type||o.type)&&(o.type&&a.removeClass("alert-"+o.type),t.type&&a.addClass("alert-"+t.type)),(angular.isUndefined(i.closeButton)||"0"!==i.closeButton&&"false"!==i.closeButton)&&a.prepend('')},!0):(angular.isUndefined(i.closeButton)||"0"!==i.closeButton&&"false"!==i.closeButton)&&a.prepend(''),a.addClass("alert").alert(),a.hasClass("fade")&&(a.removeClass("in"),setTimeout(function(){a.addClass("in")}));var s=i.ngRepeat&&i.ngRepeat.split(" in ").pop();a.on("close",function(t){var n;s?(t.preventDefault(),a.removeClass("in"),n=function(){a.trigger("closed"),e.$parent&&e.$parent.$apply(function(){for(var t=s.split("."),n=e.$parent,a=0;t.length>a;++a)n&&(n=n[t[a]]);n&&n.splice(e.$index,1)})},$.support.transition&&a.hasClass("fade")?a.on($.support.transition.end,n):n()):r&&(t.preventDefault(),a.removeClass("in"),n=function(){a.trigger("closed"),e.$apply(function(){r.closed=!0})},$.support.transition&&a.hasClass("fade")?a.on($.support.transition.end,n):n())})}}}]),angular.module("$strap.directives").directive("bsButton",["$parse","$timeout",function(t){"use strict";return{restrict:"A",require:"?ngModel",link:function(e,n,a,i){if(i){n.parent('[data-toggle="buttons-checkbox"], [data-toggle="buttons-radio"]').length||n.attr("data-toggle","button");var o=!!e.$eval(a.ngModel);o&&n.addClass("active"),e.$watch(a.ngModel,function(t,e){var a=!!t,i=!!e;a!==i?$.fn.button.Constructor.prototype.toggle.call(r):a&&!o&&n.addClass("active")})}n.hasClass("btn")||n.on("click.button.data-api",function(){n.button("toggle")}),n.button();var r=n.data("button");r.toggle=function(){if(!i)return $.fn.button.Constructor.prototype.toggle.call(this);var a=n.parent('[data-toggle="buttons-radio"]');a.length?(n.siblings("[ng-model]").each(function(n,a){t($(a).attr("ng-model")).assign(e,!1)}),e.$digest(),i.$modelValue||(i.$setViewValue(!i.$modelValue),e.$digest())):e.$apply(function(){i.$setViewValue(!i.$modelValue)})}}}}]).directive("bsButtonsCheckbox",["$parse",function(){"use strict";return{restrict:"A",require:"?ngModel",compile:function(t){t.attr("data-toggle","buttons-checkbox").find("a, button").each(function(t,e){$(e).attr("bs-button","")})}}}]).directive("bsButtonsRadio",["$parse",function(){"use strict";return{restrict:"A",require:"?ngModel",compile:function(t,e){return t.attr("data-toggle","buttons-radio"),e.ngModel||t.find("a, button").each(function(t,e){$(e).attr("bs-button","")}),function(t,e,n,a){a&&(e.find("[value]").button().filter('[value="'+t.$eval(n.ngModel)+'"]').addClass("active"),e.on("click.button.data-api",function(e){t.$apply(function(){a.$setViewValue($(e.target).closest("button").attr("value"))})}),t.$watch(n.ngModel,function(a,i){if(a!==i){var o=e.find('[value="'+t.$eval(n.ngModel)+'"]');o.length&&$.fn.button.Constructor.prototype.toggle.call(o.data("button"))}}))}}}}]),angular.module("$strap.directives").directive("bsButtonSelect",["$parse","$timeout",function(t){"use strict";return{restrict:"A",require:"?ngModel",link:function(e,n,a,i){var o=t(a.bsButtonSelect);o.assign,i&&(n.text(e.$eval(a.ngModel)),e.$watch(a.ngModel,function(t){n.text(t)}));var r,s,u,c;n.bind("click",function(){r=o(e),s=i?e.$eval(a.ngModel):n.text(),u=r.indexOf(s),c=u>r.length-2?r[0]:r[u+1],console.warn(r,c),e.$apply(function(){n.text(c),i&&i.$setViewValue(c)})})}}}]),angular.module("$strap.directives").directive("bsDatepicker",["$timeout","$strap.config",function(t,e){"use strict";var n="ontouchstart"in window&&!window.navigator.userAgent.match(/PhantomJS/i),a=function a(t){return t=t||"en",{"/":"[\\/]","-":"[-]",".":"[.]"," ":"[\\s]",dd:"(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))",d:"(?:(?:[0-2]?[0-9]{1})|(?:[3][01]{1}))",mm:"(?:[0]?[1-9]|[1][012])",m:"(?:[0]?[1-9]|[1][012])",DD:"(?:"+$.fn.datepicker.dates[t].days.join("|")+")",D:"(?:"+$.fn.datepicker.dates[t].daysShort.join("|")+")",MM:"(?:"+$.fn.datepicker.dates[t].months.join("|")+")",M:"(?:"+$.fn.datepicker.dates[t].monthsShort.join("|")+")",yyyy:"(?:(?:[1]{1}[0-9]{1}[0-9]{1}[0-9]{1})|(?:[2]{1}[0-9]{3}))(?![[0-9]])",yy:"(?:(?:[0-9]{1}[0-9]{1}))(?![[0-9]])"}},i=function i(t,e){var n,i=t,o=a(e);return n=0,angular.forEach(o,function(t,e){i=i.split(e).join("${"+n+"}"),n++}),n=0,angular.forEach(o,function(t){i=i.split("${"+n+"}").join(t),n++}),RegExp("^"+i+"$",["i"])};return{restrict:"A",require:"?ngModel",link:function(t,a,o,r){var s=e.datepicker||{},u=o.language||s.language||"en",c=o.dateFormat||s.format||$.fn.datepicker.dates[u]&&$.fn.datepicker.dates[u].format||"mm/dd/yyyy",l=n?"yyyy/mm/dd":i(c,u);if(r&&r.$parsers.unshift(function(t){return!t||l.test(t)?(r.$setValidity("date",!0),t):(r.$setValidity("date",!1),void 0)}),n&&"text"===a.prop("type"))a.prop("type","date"),a.on("change",function(){t.$apply(function(){r.$setViewValue(a.val())})});else{r&&a.on("changeDate",function(){t.$apply(function(){r.$setViewValue(a.val())})});var d=a.closest(".popover");d&&d.on("hide",function(){var t=a.data("datepicker");t&&(t.picker.remove(),a.data("datepicker",null))}),a.attr("data-toggle","datepicker"),a.datepicker({autoclose:!0,format:c,language:u,forceParse:o.forceParse||!1})}var p=a.siblings('[data-toggle="datepicker"]');p.length&&p.on("click",function(){a.trigger("focus")})}}}]),angular.module("$strap.directives").directive("bsDaterangepicker",["$timeout",function(){"use strict";return"ontouchstart"in window&&!window.navigator.userAgent.match(/PhantomJS/i),{restrict:"A",require:"?ngModel",link:function(t,e){console.log("postLink",this,arguments),window.element=e,e.attr("data-toggle","daterangepicker"),e.daterangepicker({})}}}]),angular.module("$strap.directives").directive("bsDropdown",["$parse","$compile","$timeout",function(t,e,n){"use strict";var a=function(t,e){return e||(e=['"]),angular.forEach(t,function(t,n){if(t.divider)return e.splice(n+1,0,'
  • ');var i=""+'"+(t.text||"")+"";t.submenu&&t.submenu.length&&(i+=a(t.submenu).join("\n")),i+="",e.splice(n+1,0,i)}),e};return{restrict:"EA",scope:!0,link:function(i,o,r){var s=t(r.bsDropdown),u=s(i);n(function(){!angular.isArray(u);var t=angular.element(a(u).join(""));t.insertAfter(o),e(o.next("ul.dropdown-menu"))(i)}),o.addClass("dropdown-toggle").attr("data-toggle","dropdown")}}}]),angular.module("$strap.directives").directive("bsModal",["$parse","$compile","$http","$timeout","$q","$templateCache",function(t,e,n,a,i,o){"use strict";return{restrict:"A",scope:!0,link:function(r,s,u){var c=t(u.bsModal),l=(c.assign,c(r));i.when(o.get(l)||n.get(l,{cache:!0})).then(function(t){angular.isObject(t)&&(t=t.data);var n=c(r).replace(".html","").replace(/[\/|\.|:]/g,"-")+"-"+r.$id,i=$('').attr("id",n).attr("data-backdrop",u.backdrop||!0).attr("data-keyboard",u.keyboard||!0).addClass(u.modalClass?"fade "+u.modalClass:"fade").html(t);$("body").append(i),s.attr("href","#"+n).attr("data-toggle","modal"),a(function(){e(i)(r)}),r._modal=function(t){i.modal(t)},r.hide=function(){i.modal("hide")},r.show=function(){i.modal("show")},r.dismiss=r.hide,i.on("show",function(t){r.$emit("modal-show",t)}),i.on("shown",function(t){r.$emit("modal-shown",t)}),i.on("hide",function(t){r.$emit("modal-hide",t)}),i.on("hidden",function(t){r.$emit("modal-hidden",t)})})}}}]),angular.module("$strap.directives").directive("bsNavbar",["$location",function(t){"use strict";return{restrict:"A",link:function(e,n){e.$watch(function(){return t.path()},function(t){n.find("li[data-match-route]").each(function(e,n){var a=angular.element(n),i=a.attr("data-match-route"),o=RegExp("^"+i+"$",["i"]);o.test(t)?a.addClass("active"):a.removeClass("active")})})}}}]),angular.module("$strap.directives").directive("bsPopover",["$parse","$compile","$http","$timeout","$q","$templateCache",function(t,e,n,a,i,o){"use strict";return $("body").on("keyup",function(t){27===t.keyCode&&$(".popover.in").each(function(){$(this).popover("hide")})}),{restrict:"A",scope:!0,link:function(a,r,s){var u=t(s.bsPopover),c=(u.assign,u(a)),l={};angular.isObject(c)&&(l=c),i.when(l.content||o.get(c)||n.get(c,{cache:!0})).then(function(t){angular.isObject(t)&&(t=t.data),s.unique&&r.on("show",function(){$(".popover.in").each(function(){var t=$(this),e=t.data("popover");e&&!e.$element.is(r)&&t.popover("hide")})}),s.hide&&a.$watch(s.hide,function(t,e){t?n.hide():t!==e&&n.show()}),r.popover(angular.extend({},l,{content:t,html:!0}));var n=r.data("popover");n.hasContent=function(){return this.getTitle()||t},n.getPosition=function(){var t=$.fn.popover.Constructor.prototype.getPosition.apply(this,arguments);return e(this.$tip)(a),a.$digest(),this.$tip.data("popover",this),t},a._popover=function(t){r.popover(t)},a.hide=function(){r.popover("hide")},a.show=function(){r.popover("show")},a.dismiss=a.hide})}}}]),angular.module("$strap.directives").directive("bsTabs",["$parse","$compile","$timeout",function(t,e,n){"use strict";return{restrict:"A",require:"?ngModel",scope:!0,link:function(a,i,o,r){var s=t(o.bsTabs),u=(s.assign,s(a)),c=['"],l=['
    ',"
    "];i.hide();var d=0;n(function(){angular.isArray(u)||(u=[],i.children("[data-title], [data-tab]").each(function(){var t=angular.element(this);u.push({title:a.$eval(t.data("title")||t.data("tab")),content:this.innerHTML,active:t.hasClass("active"),fade:t.hasClass("fade")})})),angular.forEach(u,function(t,e){t.active&&(d=e)}),angular.forEach(u,function(t,e){var n="tab-"+a.$id+"-"+e,i=d===e,r=o.fade||t.fade;c.splice(e+1,0,"'+t.title+""),l.splice(e+1,0,'
    '+t.content+"
    ")}),i.html(c.join("")+l.join("")).show(),e(i.children("div.tab-content"))(a)}),r&&(i.on("show",function(t){var e=$(t.target);a.$apply(function(){r.$setViewValue(e.data("index"))})}),a.$watch(o.ngModel,function(t){angular.isUndefined(t)||(d=t,setTimeout(function(){var e=i.children("ul.nav-tabs").find("li:eq("+1*t+")");e.hasClass("active")||e.children("a").tab("show")}))}))}}}]),angular.module("$strap.directives").directive("bsTimepicker",["$timeout",function(t){"use strict";var e="((?:(?:[0-1][0-9])|(?:[2][0-3])|(?:[0-9])):(?:[0-5][0-9])(?::[0-5][0-9])?(?:\\s?(?:am|AM|pm|PM))?)";return{restrict:"A",require:"?ngModel",link:function(n,a,i,o){o&&a.on("changeTime.timepicker",function(){t(function(){o.$setViewValue(a.val())})});var r=RegExp("^"+e+"$",["i"]);o.$parsers.unshift(function(t){return!t||r.test(t)?(o.$setValidity("time",!0),t):(o.$setValidity("time",!1),void 0)}),a.attr("data-toggle","timepicker"),a.parent().addClass("bootstrap-timepicker"),a.timepicker()}}}]),angular.module("$strap.directives").directive("bsTooltip",["$parse","$compile",function(t){"use strict";return{restrict:"A",scope:!0,link:function(e,n,a){var i=t(a.bsTooltip),o=(i.assign,i(e));e.$watch(a.bsTooltip,function(t,e){t!==e&&(o=t)}),a.unique&&n.on("show",function(){$(".tooltip.in").each(function(){var t=$(this),e=t.data("tooltip");e&&!e.$element.is(n)&&t.tooltip("hide")})}),n.tooltip({title:function(){return angular.isFunction(o)?o.apply(null,arguments):o},html:!0});var r=n.data("tooltip");r.show=function(){var t=$.fn.tooltip.Constructor.prototype.show.apply(this,arguments);return this.tip().data("tooltip",this),t},e._tooltip=function(t){n.tooltip(t)},e.hide=function(){n.tooltip("hide")},e.show=function(){n.tooltip("show")},e.dismiss=e.hide}}}]),angular.module("$strap.directives").directive("bsTypeahead",["$parse",function(t){"use strict";return{restrict:"A",require:"?ngModel",link:function(e,n,a,i){var o=t(a.bsTypeahead),r=(o.assign,o(e));e.$watch(a.bsTypeahead,function(t,e){t!==e&&(r=t)}),n.attr("data-provide","typeahead"),n.typeahead({source:function(){return angular.isFunction(r)?r.apply(null,arguments):r},minLength:a.minLength||1,items:a.items,updater:function(t){return i&&e.$apply(function(){i.$setViewValue(t)}),t}});var s=n.data("typeahead");s.lookup=function(){var t;return this.query=this.$element.val()||"",this.query.length