From b3e40f026d655501bfa581452c447784604ecb05 Mon Sep 17 00:00:00 2001 From: xudan Date: Fri, 6 Jul 2018 05:16:40 -0400 Subject: Move all web portal code to the new repo dovetail-webportal This is only the first step to simply copy the file here. There still need some more work to make sure all work well. All the changes will be submitted with other patches to make it easily to review. JIRA: DOVETAIL-671 Change-Id: I64d32a9df562184166b6199e2719f298687d1a0a Signed-off-by: xudan --- .../lib/angular-xeditable-0.8.0/css/xeditable.css | 220 ++ .../angular-xeditable-0.8.0/css/xeditable.min.css | 7 + .../lib/angular-xeditable-0.8.0/js/xeditable.js | 2743 ++++++++++++++++++++ .../angular-xeditable-0.8.0/js/xeditable.min.js | 7 + 4 files changed, 2977 insertions(+) create mode 100755 3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/css/xeditable.css create mode 100755 3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/css/xeditable.min.css create mode 100755 3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/js/xeditable.js create mode 100755 3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/js/xeditable.min.js (limited to '3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0') diff --git a/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/css/xeditable.css b/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/css/xeditable.css new file mode 100755 index 0000000..b86cfae --- /dev/null +++ b/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/css/xeditable.css @@ -0,0 +1,220 @@ + +/* ==== editable-form ==== */ + +/* class for single editable element */ +.editable-wrap { + display: inline-block; + white-space: pre; + margin: 0; +} + +/* remove bottom-margin for bootstrap */ +.editable-wrap .editable-controls, +.editable-wrap .editable-error { + margin-bottom: 0; +} + +/* remove bottom-margin of inputs */ +.editable-wrap .editable-controls > input, +.editable-wrap .editable-controls > select, +.editable-wrap .editable-controls > textarea { + margin-bottom: 0; +} + +/* keep buttons on the same line */ +.editable-wrap .editable-input { + display: inline-block; +} + +.editable-buttons { + display: inline-block; + vertical-align: top; +} + +.editable-buttons button { + margin-left: 5px; +} + +/* in bootstrap width: 100% => buttons go outside the box */ +.editable-input.editable-has-buttons { + width: auto; +} + +/* ==== editable-text ==== */ + +/* fix padding issue on typeahead */ +.editable-text { + white-space: nowrap; +} + +/* ==== editable-bsdate ==== */ + +/* fix padding issue on bsdate popup */ +.editable-bsdate { + white-space: nowrap; +} + +/* ==== editable-bstime ==== */ + +/* fix padding issue on bstime */ +.editable-bstime { + white-space: nowrap; +} + +/* workaround for bootstrap that sets width: 100% and inputs become too wide */ +.editable-bstime .editable-input input[type="text"] { + width: 46px; +} + +/* less padding for .well */ +.editable-bstime .well-small { + margin-bottom: 0; + padding: 10px; +} + +/* ==== editable-range ==== */ + +.editable-range output { + display: inline-block; + min-width: 30px; + vertical-align: top; + text-align: center; +} + +/* ==== editable-color ==== */ + +.editable-color input[type="color"] { + width: 50px; +} + + +/* ==== editable-checkbox ==== */ +/* ==== editable-checklist ==== */ +/* ==== editable-radiolist ==== */ + +.editable-checkbox label span, +.editable-checklist label span, +.editable-radiolist label span { + margin-left: 7px; + margin-right: 10px; +} + +/* ==== element ==== */ + +/* hiding element */ +.editable-hide { + display: none !important; +} + +.editable-click, +a.editable-click { + text-decoration: none; + color: #428bca; + border-bottom: dashed 1px #428bca; +} + +.editable-click:hover, +a.editable-click:hover { + text-decoration: none; + color: #2a6496; + border-bottom-color: #2a6496; +} + +/* editable-empty */ +.editable-empty, +.editable-empty:hover, +.editable-empty:focus, +a.editable-empty, +a.editable-empty:hover, +a.editable-empty:focus { + font-style: italic; + color: #DD1144; + text-decoration: none; +} + +/* ui-bootstrap editable popover */ +.ui-popover-wrapper a { + /* make the link always show up */ + display: inline !important; +} + +.ui-popover-wrapper form { + display: none !important; +} + +/* editable popover */ +.popover-wrapper > a { + /* make the link always show up */ + display: inline !important; +} + +.popover-wrapper { + /* make absolutely positioned children constrained to this box*/ + display: inline; + position: relative; +} + +.popover-wrapper form { + position: absolute; + top: -53px; + background: #FFF; + border: 1px solid #AAA; + border-radius: 5px; + padding: 7px; + width: auto; + display: inline-block; + left: 50%; + z-index: 101; +} + +.popover-wrapper form:before { + content:""; + width: 0; + height: 0; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-top: 10px solid #AAA; + position:absolute; + bottom:-10px; +} + +.popover-wrapper form:after { + content:""; + width:0; + height:0; + border-left: 9px solid transparent; + border-right: 9px solid transparent; + border-top: 9px solid #FFF; + position:absolute; + bottom:-9px; +} + + +@media screen and (max-width: 750px) { + .popover-wrapper form { + margin-left: -60px; + } + + .popover-wrapper form:before { + left:50px; + } + + .popover-wrapper form:after { + left:51px; + } +} + +@media screen and (min-width: 750px) { + .popover-wrapper form { + margin-left: -110px; + } + + .popover-wrapper form:before { + left:100px; + } + + .popover-wrapper form:after { + left:101px; + } +} + diff --git a/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/css/xeditable.min.css b/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/css/xeditable.min.css new file mode 100755 index 0000000..b19ca26 --- /dev/null +++ b/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/css/xeditable.min.css @@ -0,0 +1,7 @@ +/*! +angular-xeditable - 0.8.0 +Edit-in-place for angular.js +Build date: 2017-06-06 +*/ + +.editable-wrap{display:inline-block;white-space:pre;margin:0}.editable-wrap .editable-controls,.editable-wrap .editable-error{margin-bottom:0}.editable-wrap .editable-controls>input,.editable-wrap .editable-controls>select,.editable-wrap .editable-controls>textarea{margin-bottom:0}.editable-wrap .editable-input{display:inline-block}.editable-buttons{display:inline-block;vertical-align:top}.editable-buttons button{margin-left:5px}.editable-input.editable-has-buttons{width:auto}.editable-text{white-space:nowrap}.editable-bsdate{white-space:nowrap}.editable-bstime{white-space:nowrap}.editable-bstime .editable-input input[type=text]{width:46px}.editable-bstime .well-small{margin-bottom:0;padding:10px}.editable-range output{display:inline-block;min-width:30px;vertical-align:top;text-align:center}.editable-color input[type=color]{width:50px}.editable-checkbox label span,.editable-checklist label span,.editable-radiolist label span{margin-left:7px;margin-right:10px}.editable-hide{display:none!important}.editable-click,a.editable-click{text-decoration:none;color:#428bca;border-bottom:dashed 1px #428bca}.editable-click:hover,a.editable-click:hover{text-decoration:none;color:#2a6496;border-bottom-color:#2a6496}.editable-empty,.editable-empty:hover,.editable-empty:focus,a.editable-empty,a.editable-empty:hover,a.editable-empty:focus{font-style:italic;color:#D14;text-decoration:none}.ui-popover-wrapper a{display:inline!important}.ui-popover-wrapper form{display:none!important}.popover-wrapper>a{display:inline!important}.popover-wrapper{display:inline;position:relative}.popover-wrapper form{position:absolute;top:-53px;background:#FFF;border:1px solid #AAA;border-radius:5px;padding:7px;width:auto;display:inline-block;left:50%;z-index:101}.popover-wrapper form:before{content:"";width:0;height:0;border-left:10px solid transparent;border-right:10px solid transparent;border-top:10px solid #AAA;position:absolute;bottom:-10px}.popover-wrapper form:after{content:"";width:0;height:0;border-left:9px solid transparent;border-right:9px solid transparent;border-top:9px solid #FFF;position:absolute;bottom:-9px}@media screen and (max-width:750px){.popover-wrapper form{margin-left:-60px}.popover-wrapper form:before{left:50px}.popover-wrapper form:after{left:51px}}@media screen and (min-width:750px){.popover-wrapper form{margin-left:-110px}.popover-wrapper form:before{left:100px}.popover-wrapper form:after{left:101px}} \ No newline at end of file diff --git a/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/js/xeditable.js b/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/js/xeditable.js new file mode 100755 index 0000000..435b2c3 --- /dev/null +++ b/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/js/xeditable.js @@ -0,0 +1,2743 @@ +/*! +angular-xeditable - 0.8.0 +Edit-in-place for angular.js +Build date: 2017-06-06 +*/ +/** + * Angular-xeditable module + * + */ +angular.module('xeditable', []) + + +/** + * Default options. + * + * @namespace editable-options + */ +//todo: maybe better have editableDefaults, not options... +.value('editableOptions', { + /** + * Theme. Possible values `bs3`, `bs2`, `default`. + * Default is `default` + * + * @var {string} theme + * @memberOf editable-options + */ + theme: 'default', + /** + * icon_set. Possible values `font-awesome`, `default`. + * Default is `default` + * + * @var {string} icon set + * @memberOf editable-options + */ + icon_set: 'default', + /** + * Whether to show buttons for single editable element. + * Possible values `right`, `no`. + * Default is `right` + * + * @var {string} buttons + * @memberOf editable-options + */ + buttons: 'right', + /** + * Default value for `blur` attribute of single editable element. + * Can be `cancel|submit|ignore`. + * Default is `cancel` + * + * @var {string} blurElem + * @memberOf editable-options + */ + blurElem: 'cancel', + /** + * Default value for `blur` attribute of editable form. + * Can be `cancel|submit|ignore`. + * Default is `ignore`. + * + * @var {string} blurForm + * @memberOf editable-options + */ + blurForm: 'ignore', + /** + * How input elements get activated. Possible values: `focus|select|none`. + * Default is `focus` + * + * @var {string} activate + * @memberOf editable-options + */ + activate: 'focus', + /** + * Whether to disable x-editable. Can be overloaded on each element. + * Default is `false` + * + * @var {boolean} isDisabled + * @memberOf editable-options + */ + isDisabled: false, + + /** + * Event, on which the edit mode gets activated. + * Can be any event. + * Default is `click` + * + * @var {string} activationEvent + * @memberOf editable-options + */ + activationEvent: 'click', + + /** + * The default title of the submit button. + * Default is `Submit` + * + * @var {string} submitButtonTitle + * @memberOf editable-options + */ + submitButtonTitle: 'Submit', + + /** + * The default aria label of the submit button. + * Default is `Submit` + * + * @var {string} submitButtonAriaLabel + * @memberOf editable-options + */ + submitButtonAriaLabel: 'Submit', + + /** + * The default title of the cancel button. + * Default is `Cancel` + * + * @var {string} cancelButtonTitle + * @memberOf editable-options + */ + cancelButtonTitle: 'Cancel', + + /** + * The default aria label of the cancel button. + * Default is `Cancel` + * + * @var {string} cancelButtonAriaLabel + * @memberOf editable-options + */ + cancelButtonAriaLabel: 'Cancel', + + /** + * The default title of the clear button. + * Default is `Clear` + * + * @var {string} clearButtonTitle + * @memberOf editable-options + */ + clearButtonTitle: 'Clear', + + /** + * The default aria label of the clear button. + * Default is `Clear` + * + * @var {string} clearButtonAriaLabel + * @memberOf editable-options + */ + clearButtonAriaLabel: 'Clear', + + /** + * Whether to display the clear button. + * Default is `false` + * + * @var {boolean} displayClearButton + * @memberOf editable-options + */ + displayClearButton: false +}); + +/* + Angular-ui bootstrap datepicker + http://angular-ui.github.io/bootstrap/#/datepicker + */ +angular.module('xeditable').directive('editableBsdate', ['editableDirectiveFactory', '$injector', '$parse', + function(editableDirectiveFactory, $injector, $parse) { + + // Constants from Angular-ui bootstrap datepicker + uibDatepickerConfig = $injector.get('uibDatepickerConfig'); + uibDatepickerPopupConfig = $injector.get('uibDatepickerPopupConfig'); + + var popupAttrNames = [ + ['eIsOpen', 'is-open'], + ['eDateDisabled', 'date-disabled'], + ['eDatepickerPopup', 'uib-datepicker-popup'], + ['eShowButtonBar', 'show-button-bar'], + ['eCurrentText', 'current-text'], + ['eClearText', 'clear-text'], + ['eCloseText', 'close-text'], + ['eCloseOnDateSelection', 'close-on-date-selection'], + ['eDatepickerAppendToBody', 'datepicker-append-to-body'], + ['eOnOpenFocus', 'on-open-focus'], + ['eName', 'name'], + ['eDateDisabled', 'date-disabled'], + ['eAltInputFormats', 'alt-input-formats'] + ]; + + var dateOptionsNames = [ + ['eFormatDay', 'formatDay'], + ['eFormatMonth', 'formatMonth'], + ['eFormatYear', 'formatYear'], + ['eFormatDayHeader', 'formatDayHeader'], + ['eFormatDayTitle', 'formatDayTitle'], + ['eFormatMonthTitle', 'formatMonthTitle'], + ['eMaxMode', 'maxMode'], + ['eMinMode', 'minMode'], + ['eDatepickerMode', 'datepickerMode'] + ]; + + return editableDirectiveFactory({ + directiveName: 'editableBsdate', + inputTpl: '
', + render: function() { + /** This basically renders a datepicker as in the example shown in + ** http://angular-ui.github.io/bootstrap/#/datepicker + ** The attributes are all the same as in the bootstrap-ui datepicker with e- as prefix + **/ + this.parent.render.call(this); + + var attrs = this.attrs; + var scope = this.scope; + + var inputDatePicker = angular.element(''); + + inputDatePicker.attr('uib-datepicker-popup', attrs.eDatepickerPopupXEditable || uibDatepickerPopupConfig.datepickerPopup ); + inputDatePicker.attr('year-range', attrs.eYearRange || 20); + inputDatePicker.attr('ng-readonly', attrs.eReadonly || false); + + for (var i = popupAttrNames.length - 1; i >= 0; i--) { + var popupAttr = attrs[popupAttrNames[i][0]]; + if (typeof popupAttr !== 'undefined') { + inputDatePicker.attr(popupAttrNames[i][1], popupAttr); + } + } + + if (attrs.eNgChange) { + inputDatePicker.attr('ng-change', attrs.eNgChange); + this.inputEl.removeAttr('ng-change'); + } + + if (attrs.eStyle) { + inputDatePicker.attr('style', attrs.eStyle); + this.inputEl.removeAttr('style'); + } + + var dateOptions = { + maxDate: scope.$eval(attrs.eMaxDate) || uibDatepickerConfig.maxDate, + minDate: scope.$eval(attrs.eMinDate) || uibDatepickerConfig.minDate, + showWeeks: attrs.eShowWeeks ? attrs.eShowWeeks.toLowerCase() === 'true' : uibDatepickerConfig.showWeeks, + startingDay: attrs.eStartingDay || 0, + initDate: scope.$eval(attrs.eInitDate) || new Date() + }; + + if (attrs.eDatepickerOptions) { + var eDatepickerOptions = $parse(attrs.eDatepickerOptions)(scope); + angular.extend(dateOptions, eDatepickerOptions); + } + + for (var z = dateOptionsNames.length - 1; z >= 0; z--) { + var doAttr = attrs[dateOptionsNames[z][0]]; + if (typeof doAttr !== 'undefined') { + dateOptions[dateOptionsNames[z][1]] = doAttr; + } + } + + scope.dateOptions = dateOptions; + + var showCalendarButton = angular.isDefined(attrs.eShowCalendarButton) ? attrs.eShowCalendarButton : "true"; + + //See if calendar button should be displayed + if (showCalendarButton === "true") { + var buttonDatePicker = angular.element(''); + var buttonWrapper = angular.element(''); + + buttonDatePicker.attr('ng-click', attrs.eNgClick); + + buttonWrapper.append(buttonDatePicker); + + this.inputEl.append(buttonWrapper); + } else { + //If no calendar button, display calendar popup on click of input field + inputDatePicker.attr('ng-click', attrs.eNgClick); + } + + inputDatePicker.attr('datepicker-options', "dateOptions"); + + this.inputEl.prepend(inputDatePicker); + + this.inputEl.removeAttr('class'); + this.inputEl.removeAttr('ng-click'); + this.inputEl.removeAttr('is-open'); + this.inputEl.removeAttr('init-date'); + this.inputEl.removeAttr('datepicker-popup'); + this.inputEl.removeAttr('required'); + this.inputEl.removeAttr('ng-model'); + this.inputEl.removeAttr('date-picker-append-to-body'); + this.inputEl.removeAttr('name'); + this.inputEl.attr('class','input-group'); + }, + autosubmit: function() { + var self = this; + self.inputEl.bind('change', function() { + setTimeout(function() { + self.scope.$apply(function() { + self.scope.$form.$submit(); + }); + }, 500); + }); + + self.inputEl.bind('keydown', function(e) { + //submit on tab + if (e.keyCode === 9 && self.editorEl.attr('blur') === 'submit') { + self.scope.$apply(function() { + self.scope.$form.$submit(); + }); + } + }); + } + }); +}]); + +/* +Angular-ui bootstrap editable timepicker +http://angular-ui.github.io/bootstrap/#/timepicker +*/ +angular.module('xeditable').directive('editableBstime', ['editableDirectiveFactory', + function(editableDirectiveFactory) { + return editableDirectiveFactory({ + directiveName: 'editableBstime', + inputTpl: '
', + render: function() { + this.parent.render.call(this); + + // timepicker can't update model when ng-model set directly to it + // see: https://github.com/angular-ui/bootstrap/issues/1141 + // so we wrap it into DIV + var div = angular.element('
'); + + // move ng-model to wrapping div + div.attr('ng-model', this.inputEl.attr('ng-model')); + this.inputEl.removeAttr('ng-model'); + + // move ng-change to wrapping div + if(this.attrs.eNgChange) { + div.attr('ng-change', this.inputEl.attr('ng-change')); + this.inputEl.removeAttr('ng-change'); + } + + // wrap + this.inputEl.wrap(div); + } + }); +}]); +//checkbox +angular.module('xeditable').directive('editableCheckbox', ['editableDirectiveFactory', + function(editableDirectiveFactory) { + return editableDirectiveFactory({ + directiveName: 'editableCheckbox', + inputTpl: '', + render: function() { + this.parent.render.call(this); + this.inputEl.wrap(''); + + if (this.attrs.eTitle) { + this.inputEl.parent().append('' + this.attrs.eTitle + ''); + } + }, + autosubmit: function() { + var self = this; + self.inputEl.bind('change', function() { + setTimeout(function() { + self.scope.$apply(function() { + self.scope.$form.$submit(); + }); + }, 500); + }); + } + }); +}]); + +// checklist +angular.module('xeditable').directive('editableChecklist', [ + 'editableDirectiveFactory', + 'editableNgOptionsParser', + function(editableDirectiveFactory, editableNgOptionsParser) { + return editableDirectiveFactory({ + directiveName: 'editableChecklist', + inputTpl: '', + useCopy: true, + render: function() { + this.parent.render.call(this); + var parsed = editableNgOptionsParser(this.attrs.eNgOptions); + var ngChangeHtml = ''; + var ngChecklistComparatorHtml = ''; + + if (this.attrs.eNgChange) { + ngChangeHtml = ' ng-change="' + this.attrs.eNgChange + '"'; + } + + if (this.attrs.eChecklistComparator) { + ngChecklistComparatorHtml = ' checklist-comparator="' + this.attrs.eChecklistComparator + '"'; + } + + var html = ''; + + this.inputEl.removeAttr('ng-model'); + this.inputEl.removeAttr('ng-options'); + this.inputEl.removeAttr('ng-change'); + this.inputEl.removeAttr('checklist-comparator'); + this.inputEl.html(html); + } + }); +}]); + +angular.module('xeditable').directive('editableCombodate', ['editableDirectiveFactory', 'editableCombodate', + function(editableDirectiveFactory, editableCombodate) { + return editableDirectiveFactory({ + directiveName: 'editableCombodate', + inputTpl: '', + render: function() { + this.parent.render.call(this); + + var options = { + value: new Date(this.scope.$data) + }; + var self = this; + angular.forEach(["format", "template", "minYear", "maxYear", "yearDescending", "minuteStep", "secondStep", "firstItem", "errorClass", "customClass", "roundTime", "smartDays"], function(name) { + + var attrName = "e" + name.charAt(0).toUpperCase() + name.slice(1); + + if (attrName in self.attrs) { + if (name == "minYear" || name == "maxYear" || name == "minuteStep" || name == "secondStep") { + options[name] = parseInt(self.attrs[attrName], 10); + } else { + options[name] = self.attrs[attrName]; + } + } + }); + + var combodate = editableCombodate.getInstance(this.inputEl, options); + combodate.$widget.find('select').bind('change', function(e) { + //.replace is so this works in Safari + self.scope.$data = combodate.getValue() ? + (new Date(combodate.getValue().replace(/-/g, "/"))).toISOString() : null; + }); + } + }); + } +]); + +/* +Input types: text|password|email|tel|number|url|search|color|date|datetime|datetime-local|time|month|week|file +*/ + +(function() { + + var camelCase = function(dashDelimitedString) { + return dashDelimitedString.toLowerCase().replace(/-(.)/g, function(match, word) { + return word.toUpperCase(); + }); + }; + + var types = 'text|password|email|tel|number|url|search|color|date|datetime|datetime-local|time|month|week|file'.split('|'); + + //todo: datalist + + // generate directives + angular.forEach(types, function(type) { + var directiveName = camelCase('editable' + '-' + type); + angular.module('xeditable').directive(directiveName, ['editableDirectiveFactory', + function(editableDirectiveFactory) { + return editableDirectiveFactory({ + directiveName: directiveName, + inputTpl: '', + render: function() { + this.parent.render.call(this); + + //Add bootstrap simple input groups + if (this.attrs.eInputgroupleft || this.attrs.eInputgroupright) { + this.inputEl.wrap('
'); + + if (this.attrs.eInputgroupleft) { + var inputGroupLeft = angular.element('' + this.attrs.eInputgroupleft + ''); + this.inputEl.parent().prepend(inputGroupLeft); + } + + if (this.attrs.eInputgroupright) { + var inputGroupRight = angular.element('' + this.attrs.eInputgroupright + ''); + this.inputEl.parent().append(inputGroupRight); + } + } + + // Add label to the input + if (this.attrs.eLabel) { + var label = angular.element(''); + if (this.attrs.eInputgroupleft || this.attrs.eInputgroupright) { + this.inputEl.parent().parent().prepend(label); + } else { + this.inputEl.parent().prepend(label); + } + } + + // Add classes to the form + if (this.attrs.eFormclass) { + this.editorEl.addClass(this.attrs.eFormclass); + } + }, + autosubmit: function() { + var self = this; + self.inputEl.bind('keydown', function(e) { + //submit on tab + if (e.keyCode === 9 && self.editorEl.attr('blur') === 'submit') { + self.scope.$apply(function() { + self.scope.$form.$submit(); + }); + } + }); + } + }); + }]); + }); + + //`range` is bit specific + angular.module('xeditable').directive('editableRange', ['editableDirectiveFactory', '$interpolate', + function(editableDirectiveFactory, $interpolate) { + return editableDirectiveFactory({ + directiveName: 'editableRange', + inputTpl: '', + render: function() { + this.parent.render.call(this); + this.inputEl.after('' + $interpolate.startSymbol() + '$data' + $interpolate.endSymbol() + ''); + } + }); + }]); + +}()); + + +/* + Tags input directive for AngularJS + https://github.com/mbenford/ngTagsInput + */ +angular.module('xeditable').directive('editableTagsInput', ['editableDirectiveFactory', 'editableUtils', + function(editableDirectiveFactory, editableUtils) { + var dir = editableDirectiveFactory({ + directiveName: 'editableTagsInput', + inputTpl: '', + useCopy: true, + render: function () { + this.parent.render.call(this); + this.inputEl.append(editableUtils.rename('auto-complete', this.attrs.$autoCompleteElement)); + this.inputEl.removeAttr('ng-model'); + this.inputEl.attr('ng-model', '$parent.$data'); + } + }); + + var linkOrg = dir.link; + + dir.link = function (scope, el, attrs, ctrl) { + var autoCompleteEl = el.find('editable-tags-input-auto-complete'); + + attrs.$autoCompleteElement = autoCompleteEl.clone(); + + autoCompleteEl.remove(); + + return linkOrg(scope, el, attrs, ctrl); + }; + + return dir; +}]); +// radiolist +angular.module('xeditable').directive('editableRadiolist', [ + 'editableDirectiveFactory', + 'editableNgOptionsParser', + '$interpolate', + function(editableDirectiveFactory, editableNgOptionsParser, $interpolate) { + return editableDirectiveFactory({ + directiveName: 'editableRadiolist', + inputTpl: '', + render: function() { + this.parent.render.call(this); + var parsed = editableNgOptionsParser(this.attrs.eNgOptions), + ngChangeHtml = '', + ngNameHtml = ''; + + if (this.attrs.eNgChange) { + ngChangeHtml = ' ng-change="' + this.attrs.eNgChange + '"'; + } + + if (this.attrs.eName) { + ngNameHtml = ' name="' + this.attrs.eName + '"'; + } + + var html = ''; + + this.inputEl.removeAttr('ng-model'); + this.inputEl.removeAttr('ng-options'); + this.inputEl.removeAttr('ng-change'); + this.inputEl.html(html); + }, + autosubmit: function() { + var self = this; + self.inputEl.bind('change', function() { + setTimeout(function() { + self.scope.$apply(function() { + self.scope.$form.$submit(); + }); + }, 500); + }); + } + }); +}]); + +//select +angular.module('xeditable').directive('editableSelect', ['editableDirectiveFactory', + function(editableDirectiveFactory) { + return editableDirectiveFactory({ + directiveName: 'editableSelect', + inputTpl: '', + render: function() { + this.parent.render.call(this); + + if (this.attrs.ePlaceholder) { + var placeholder = angular.element(''); + this.inputEl.append(placeholder); + } + }, + autosubmit: function() { + var self = this; + + if (!self.attrs.hasOwnProperty("eMultiple")) { + self.inputEl.bind('change', function () { + self.scope.$apply(function () { + self.scope.$form.$submit(); + }); + }); + } + } + }); +}]); +//textarea +angular.module('xeditable').directive('editableTextarea', ['editableDirectiveFactory', + function(editableDirectiveFactory) { + return editableDirectiveFactory({ + directiveName: 'editableTextarea', + inputTpl: '', + addListeners: function() { + var self = this; + self.parent.addListeners.call(self); + // submit textarea by ctrl+enter even with buttons + if (self.single && self.buttons !== 'no') { + self.autosubmit(); + } + }, + autosubmit: function() { + var self = this; + self.inputEl.bind('keydown', function(e) { + if (self.attrs.submitOnEnter) { + if (e.keyCode === 13 && !e.shiftKey) { + self.scope.$apply(function() { + self.scope.$form.$submit(); + }); + } + } else if ((e.ctrlKey || e.metaKey) && (e.keyCode === 13) || + (e.keyCode === 9 && self.editorEl.attr('blur') === 'submit')) { + self.scope.$apply(function() { + self.scope.$form.$submit(); + }); + } + }); + } + }); +}]); + +/* + jQuery UI Datepicker for AngularJS + https://github.com/angular-ui/ui-date + */ +angular.module('xeditable').directive('editableUidate', ['editableDirectiveFactory', + function(editableDirectiveFactory) { + return editableDirectiveFactory({ + directiveName: 'editableUidate', + inputTpl: '', + render: function() { + this.parent.render.call(this); + this.inputEl.attr('ui-date', this.attrs.eUiDate); + this.inputEl.attr('placeholder', this.attrs.ePlaceholder); + } + }); +}]); + +/* + AngularJS-native version of Select2 and Selectize + https://github.com/angular-ui/ui-select + */ +angular.module('xeditable').directive('editableUiSelect',['editableDirectiveFactory', 'editableUtils', + function(editableDirectiveFactory, editableUtils) { + var dir = editableDirectiveFactory({ + directiveName: 'editableUiSelect', + inputTpl: '', + render: function () { + this.parent.render.call(this); + this.inputEl.append(editableUtils.rename('ui-select-match', this.attrs.$matchElement)); + this.inputEl.append(editableUtils.rename('ui-select-choices', this.attrs.$choicesElement)); + this.inputEl.removeAttr('ng-model'); + this.inputEl.attr('ng-model', '$parent.$parent.$data'); + }, + autosubmit: function() { + var self = this; + self.inputEl.bind('change', function() { + setTimeout(function() { + self.scope.$apply(function() { + self.scope.$form.$submit(); + }); + }, 500); + }); + + self.inputEl.bind('keydown', function(e) { + //submit on tab + if (e.keyCode === 9 && self.editorEl.attr('blur') === 'submit') { + self.scope.$apply(function() { + self.scope.$form.$submit(); + }); + } + }); + } + }); + + var linkOrg = dir.link; + + dir.link = function (scope, el, attrs, ctrl) { + var matchEl = el.find('editable-ui-select-match'); + var choicesEl = el.find('editable-ui-select-choices'); + + attrs.$matchElement = matchEl.clone(); + attrs.$choicesElement = choicesEl.clone(); + + matchEl.remove(); + choicesEl.remove(); + + return linkOrg(scope, el, attrs, ctrl); + }; + + return dir; + }]); +/** + * EditableController class. + * Attached to element with `editable-xxx` directive. + * + * @namespace editable-element + */ +/* +TODO: this file should be refactored to work more clear without closures! +*/ +angular.module('xeditable').factory('editableController', + ['$q', 'editableUtils', + function($q, editableUtils) { + + //EditableController function + EditableController.$inject = ['$scope', '$attrs', '$element', '$parse', 'editableThemes', 'editableIcons', 'editableOptions', '$rootScope', '$compile', '$q', '$sce', '$templateCache']; + function EditableController($scope, $attrs, $element, $parse, editableThemes, editableIcons, editableOptions, $rootScope, $compile, $q, $sce, $templateCache) { + var valueGetter; + + //if control is disabled - it does not participate in waiting process + var inWaiting; + + var self = this; + + self.scope = $scope; + self.elem = $element; + self.attrs = $attrs; + self.inputEl = null; + self.editorEl = null; + self.single = true; + self.error = ''; + self.theme = editableThemes[$attrs.editableTheme] || editableThemes[editableOptions.theme] || editableThemes['default']; + self.parent = {}; + + //will be undefined if icon_set is default and theme is default + var theme_name = $attrs.editableTheme || editableOptions.theme || 'default'; + // The theme_name will not be correct if the theme set in options is unavailable + // However, in that case an undefined icon_set is not that bad... + var icon_set_option = $attrs.editableIconSet || editableOptions.icon_set; + self.icon_set = icon_set_option === 'default' ? editableIcons.default[theme_name] : editableIcons.external[icon_set_option]; + + //to be overwritten by directive + self.inputTpl = ''; + self.directiveName = ''; + + // with majority of controls copy is not needed, but.. + // copy MUST NOT be used for `select-multiple` with objects as items + // copy MUST be used for `checklist` + self.useCopy = false; + + //runtime (defaults) + self.single = null; + + /** + * Attributes defined with `e-*` prefix automatically transferred from original element to + * control. + * For example, if you set ` + * then input will appear as ``. + * See [demo](#text-customize). + * + * @var {any|attribute} e-* + * @memberOf editable-element + */ + + /** + * Whether to show ok/cancel buttons. Values: `right|no`. + * If set to `no` control automatically submitted when value changed. + * If control is part of form buttons will never be shown. + * + * @var {string|attribute} buttons + * @memberOf editable-element + */ + self.buttons = 'right'; + + /** + * Whether to show the editable element in a ui-bootstrap popover. Values: `true|false`. + * + * @var {boolean|attribute} popover + * @memberOf editable-element + */ + self.popover = false; + + /** + * Action when control losses focus. Values: `cancel|submit|ignore`. + * Has sense only for single editable element. + * Otherwise, if control is part of form - you should set `blur` of form, not of individual element. + * + * @var {string|attribute} blur + * @memberOf editable-element + */ + // no real `blur` property as it is transferred to editable form + + //init + self.init = function(single) { + self.single = single; + + self.name = $attrs.eName || $attrs[self.directiveName]; + /* + if(!$attrs[directiveName] && !$attrs.eNgModel && ($attrs.eValue === undefined)) { + throw 'You should provide value for `'+directiveName+'` or `e-value` in editable element!'; + } + */ + if($attrs[self.directiveName]) { + valueGetter = $parse($attrs[self.directiveName]); + } else { + throw 'You should provide value for `'+self.directiveName+'` in editable element!'; + } + + // settings for single and non-single + if (!self.single) { + // hide buttons for non-single + self.buttons = 'no'; + } else { + self.buttons = self.attrs.buttons || editableOptions.buttons; + } + + //if name defined --> watch changes and update $data in form + if($attrs.eName) { + self.scope.$watch('$data', function(newVal){ + self.scope.$form.$data[$attrs.eName] = newVal; + }); + } + + /** + * Called when control is shown. + * See [demo](#select-remote). + * + * @var {method|attribute} onshow + * @memberOf editable-element + */ + if($attrs.onshow) { + self.onshow = function() { + return self.catchError($parse($attrs.onshow)($scope)); + }; + } + + /** + * Called when control is hidden after both save or cancel. + * + * @var {method|attribute} onhide + * @memberOf editable-element + */ + if ($attrs.onhide) { + self.onhide = function() { + return $parse($attrs.onhide)($scope); + }; + } + + /** + * Called when control is cancelled. + * + * @var {method|attribute} oncancel + * @memberOf editable-element + */ + if ($attrs.oncancel) { + self.oncancel = function() { + return $parse($attrs.oncancel)($scope); + }; + } + + /** + * Called during submit before value is saved to model. + * See [demo](#onbeforesave). + * + * @var {method|attribute} onbeforesave + * @memberOf editable-element + */ + if ($attrs.onbeforesave) { + self.onbeforesave = function() { + return self.catchError($parse($attrs.onbeforesave)($scope)); + }; + } + + /** + * Called during submit after value is saved to model. + * See [demo](#onaftersave). + * + * @var {method|attribute} onaftersave + * @memberOf editable-element + */ + if ($attrs.onaftersave) { + self.onaftersave = function() { + return self.catchError($parse($attrs.onaftersave)($scope)); + }; + } + + if ($attrs.popover) { + self.popover = self.attrs.popover; + } + + // watch change of model to update editable element + // now only add/remove `editable-empty` class. + // Initially this method called with newVal = undefined, oldVal = undefined + // so no need initially call handleEmpty() explicitly + $scope.$parent.$watch($attrs[self.directiveName], function(newVal, oldVal) { + self.setLocalValue(); + self.handleEmpty(); + }); + }; + + self.render = function() { + var theme = self.theme; + + //build input + self.inputEl = angular.element(self.inputTpl); + + //build controls + self.controlsEl = angular.element(theme.controlsTpl); + self.controlsEl.append(self.inputEl); + + //build buttons + if(self.buttons !== 'no') { + self.buttonsEl = angular.element(theme.buttonsTpl); + self.submitEl = angular.element(theme.submitTpl); + self.resetEl = angular.element(theme.resetTpl); + self.cancelEl = angular.element(theme.cancelTpl); + self.submitEl.attr('title', editableOptions.submitButtonTitle); + self.submitEl.attr('aria-label', editableOptions.submitButtonAriaLabel); + self.cancelEl.attr('title', editableOptions.cancelButtonTitle); + self.cancelEl.attr('aria-label', editableOptions.cancelButtonAriaLabel); + self.resetEl.attr('title', editableOptions.clearButtonTitle); + self.resetEl.attr('aria-label', editableOptions.clearButtonAriaLabel); + + if (self.icon_set) { + self.submitEl.find('span').addClass(self.icon_set.ok); + self.cancelEl.find('span').addClass(self.icon_set.cancel); + self.resetEl.find('span').addClass(self.icon_set.clear); + } + + self.buttonsEl.append(self.submitEl).append(self.cancelEl); + + if (editableOptions.displayClearButton) { + self.buttonsEl.append(self.resetEl); + } + + self.controlsEl.append(self.buttonsEl); + + self.inputEl.addClass('editable-has-buttons'); + } + + //build error + self.errorEl = angular.element(theme.errorTpl); + self.controlsEl.append(self.errorEl); + + //build editor + self.editorEl = angular.element(self.single ? theme.formTpl : theme.noformTpl); + self.editorEl.append(self.controlsEl); + + // transfer `e-*|data-e-*|x-e-*` attributes + for (var k in $attrs.$attr) { + if(k.length <= 1) { + continue; + } + var transferAttr = false; + var nextLetter = k.substring(1, 2); + + // if starts with `e` + uppercase letter + if (k.substring(0, 1) === 'e' && nextLetter === nextLetter.toUpperCase()) { + transferAttr = k.substring(1); // cut `e` + } else { + continue; + } + + // exclude `form` and `ng-submit`, + if (transferAttr === 'Form' || transferAttr === 'NgSubmit') { + continue; + } + + var firstLetter = transferAttr.substring(0, 1); + var secondLetter = transferAttr.substring(1, 2); + + // convert back to lowercase style + if (secondLetter === secondLetter.toUpperCase() && + firstLetter === firstLetter.toUpperCase()) { + transferAttr = firstLetter.toLowerCase() + '-' + editableUtils.camelToDash(transferAttr.substring(1)); + } else { + transferAttr = firstLetter.toLowerCase() + editableUtils.camelToDash(transferAttr.substring(1)); + } + + // workaround for attributes without value (e.g. `multiple = "multiple"`) + // except for 'e-value' + var attrValue = (transferAttr !== 'value' && $attrs[k] === '') ? transferAttr : $attrs[k]; + + // set attributes to input + self.inputEl.attr(transferAttr, attrValue); + } + + self.inputEl.addClass('editable-input'); + self.inputEl.attr('ng-model', '$parent.$data'); + + // add directiveName class to editor, e.g. `editable-text` + self.editorEl.addClass(editableUtils.camelToDash(self.directiveName)); + + if (self.single) { + self.editorEl.attr('editable-form', '$form'); + // transfer `blur` to form + self.editorEl.attr('blur', self.attrs.blur || editableOptions.blurElem); + } + + if (self.popover) { + var wrapper = angular.element('
'); + wrapper.append(self.editorEl); + self.editorEl = wrapper; + $templateCache.put('popover.html', self.editorEl[0].outerHTML); + } + + //apply `postrender` method of theme + if (angular.isFunction(theme.postrender)) { + theme.postrender.call(self); + } + + }; + + // with majority of controls copy is not needed, but.. + // copy MUST NOT be used for `select-multiple` with objects as items + // copy MUST be used for `checklist` + self.setLocalValue = function() { + self.scope.$data = self.useCopy ? + angular.copy(valueGetter($scope.$parent)) : + valueGetter($scope.$parent); + }; + + // reference of the scope to use for $compile + var newScope = null; + //show + self.show = function() { + // set value of scope.$data + self.setLocalValue(); + + /* + Originally render() was inside init() method, but some directives polluting editorEl, + so it is broken on second opening. + Cloning is not a solution as jqLite can not clone with event handler's. + */ + self.render(); + + // insert into DOM + $element.after(self.editorEl); + + // compile (needed to attach ng-* events from markup) + newScope = $scope.$new(); + $compile(self.editorEl)(newScope); + + // attach listeners (`escape`, autosubmit, etc) + self.addListeners(); + + // hide element + $element.addClass('editable-hide'); + + // onshow + return self.onshow(); + }; + + //hide + self.hide = function() { + + // destroy the scope to prevent memory leak + newScope.$destroy(); + + self.controlsEl.remove(); + self.editorEl.remove(); + $element.removeClass('editable-hide'); + + if (self.popover) { + $templateCache.remove('popover.html'); + } + + // onhide + return self.onhide(); + }; + + // cancel + self.cancel = function() { + // oncancel + self.oncancel(); + // don't call hide() here as it called in form's code + }; + + /* + Called after show to attach listeners + */ + self.addListeners = function() { + // bind keyup for `escape` + self.inputEl.bind('keyup', function(e) { + if(!self.single) { + return; + } + + // todo: move this to editable-form! + switch(e.keyCode) { + // hide on `escape` press + case 27: + self.scope.$apply(function() { + self.scope.$form.$cancel(); + }); + break; + } + }); + + // autosubmit when `no buttons` + if (self.single && self.buttons === 'no') { + self.autosubmit(); + } + + // click - mark element as clicked to exclude in document click handler + self.editorEl.bind('click', function(e) { + // ignore right/middle button click + if (e.which && e.which !== 1) { + return; + } + + if (self.scope.$form.$visible) { + self.scope.$form._clicked = true; + } + }); + }; + + // setWaiting + self.setWaiting = function(value) { + if (value) { + // participate in waiting only if not disabled + inWaiting = !self.inputEl.attr('disabled') && + !self.inputEl.attr('ng-disabled') && + !self.inputEl.attr('ng-enabled'); + if (inWaiting) { + self.inputEl.attr('disabled', 'disabled'); + if(self.buttonsEl) { + self.buttonsEl.find('button').attr('disabled', 'disabled'); + } + } + } else { + if (inWaiting) { + self.inputEl.removeAttr('disabled'); + if (self.buttonsEl) { + self.buttonsEl.find('button').removeAttr('disabled'); + } + } + } + }; + + self.activate = function(start, end) { + setTimeout(function() { + var el = self.inputEl[0]; + + if (editableOptions.activate === 'focus' && el.focus) { + if (start !== undefined && start !== "" && el.setSelectionRange) { + end = end || start; + el.onfocus = function() { + setTimeout(function() { + try { + this.setSelectionRange(start,end); + } catch(e) { + //do nothing, this input doesn't support setSelectionRange + } + }.bind(this)); + }; + } + + if (self.directiveName == 'editableRadiolist' || self.directiveName == 'editableChecklist' || + self.directiveName == 'editableBsdate' || self.directiveName == 'editableTagsInput') { + //Set focus to first pristine element in the list + el.querySelector('.ng-pristine').focus(); + } else { + el.focus(); + } + } else if (editableOptions.activate === 'select') { + if (el.select){ + el.select(); + } else if (el.focus) { + el.focus(); + } + } + }, 0); + }; + + self.setError = function(msg) { + if(!angular.isObject(msg)) { + $scope.$error = $sce.trustAsHtml(msg); + self.error = msg; + } + }; + + /* + Checks that result is string or promise returned string and shows it as error message + Applied to onshow, onbeforesave, onaftersave + */ + self.catchError = function(result, noPromise) { + if (angular.isObject(result) && noPromise !== true) { + $q.when(result).then( + //success and fail handlers are equal + angular.bind(this, function(r) { + this.catchError(r, true); + }), + angular.bind(this, function(r) { + this.catchError(r, true); + }) + ); + //check $http error + } else if (noPromise && angular.isObject(result) && result.status && + (result.status !== 200) && result.data && angular.isString(result.data)) { + this.setError(result.data); + //set result to string: to let form know that there was error + result = result.data; + } else if (angular.isString(result)) { + this.setError(result); + } + return result; + }; + + self.save = function() { + valueGetter.assign($scope.$parent, + self.useCopy ? angular.copy(self.scope.$data) : self.scope.$data); + + // no need to call handleEmpty here as we are watching change of model value + // self.handleEmpty(); + }; + + /* + attach/detach `editable-empty` class to element + */ + self.handleEmpty = function() { + var val = valueGetter($scope.$parent); + var isEmpty = val === null || val === undefined || val === "" || (angular.isArray(val) && val.length === 0); + $element.toggleClass('editable-empty', isEmpty); + }; + + /* + Called when `buttons = "no"` to submit automatically + */ + self.autosubmit = angular.noop; + + self.onshow = angular.noop; + self.onhide = angular.noop; + self.oncancel = angular.noop; + self.onbeforesave = angular.noop; + self.onaftersave = angular.noop; + } + + return EditableController; +}]); + +/* +editableFactory is used to generate editable directives (see `/directives` folder) +Inside it does several things: +- detect form for editable element. Form may be one of three types: + 1. autogenerated form (for single editable elements) + 2. wrapper form (element wrapped by
tag) + 3. linked form (element has `e-form` attribute pointing to existing form) + +- attach editableController to element + +Depends on: editableController, editableFormFactory +*/ +angular.module('xeditable').factory('editableDirectiveFactory', +['$parse', '$compile', 'editableThemes', '$rootScope', '$document', 'editableController', 'editableFormController', 'editableOptions', +function($parse, $compile, editableThemes, $rootScope, $document, editableController, editableFormController, editableOptions) { + + //directive object + return function(overwrites) { + return { + restrict: 'A', + scope: true, + require: [overwrites.directiveName, '?^form'], + controller: editableController, + link: function(scope, elem, attrs, ctrl) { + // editable controller + var eCtrl = ctrl[0]; + + // form controller + var eFormCtrl; + + // this variable indicates is element is bound to some existing form, + // or it's single element who's form will be generated automatically + // By default consider single element without any linked form.ß + var hasForm = false; + + // element wrapped by form + if (ctrl[1]) { + eFormCtrl = ctrl[1]; + hasForm = attrs.eSingle === undefined; + } else if (attrs.eForm) { // element not wrapped by , but we have `e-form` attr + var getter = $parse(attrs.eForm)(scope); + if (getter) { // form exists in scope (above), e.g. editable column + eFormCtrl = getter; + hasForm = true; + } else if (elem && typeof elem.parents === "function" && elem.parents().last().find('form[name='+attrs.eForm+']').length) { // form exists below or not exist at all: check document.forms + // form is below and not processed yet + eFormCtrl = null; + hasForm = true; + } else { + // form exists below or not exist at all: check document.forms + for (var i=0; i<$document[0].forms.length;i++) { + if ($document[0].forms[i].name === attrs.eForm) { + // form is below and not processed yet + eFormCtrl = null; + hasForm = true; + break; + } + } + } + } + + /* + if(hasForm && !attrs.eName) { + throw 'You should provide `e-name` for editable element inside form!'; + } + */ + + //check for `editable-form` attr in form + /* + if(eFormCtrl && ) { + throw 'You should provide `e-name` for editable element inside form!'; + } + */ + + // store original props to `parent` before merge + angular.forEach(overwrites, function(v, k) { + if(eCtrl[k] !== undefined) { + eCtrl.parent[k] = eCtrl[k]; + } + }); + + // merge overwrites to base editable controller + angular.extend(eCtrl, overwrites); + + // x-editable can be disabled using editableOption or edit-disabled attribute + var is_disabled = function() { + return angular.isDefined(attrs.editDisabled) ? + scope.$eval(attrs.editDisabled) : + editableOptions.isDisabled; + }; + + // init editable ctrl + eCtrl.init(!hasForm); + + // publich editable controller as `$editable` to be referenced in html + scope.$editable = eCtrl; + + // add `editable` class to element + elem.addClass('editable'); + + // hasForm + if(hasForm) { + if(eFormCtrl) { + scope.$form = eFormCtrl; + if(!scope.$form.$addEditable) { + throw 'Form with editable elements should have `editable-form` attribute.'; + } + scope.$form.$addEditable(eCtrl); + } else { + // future form (below): add editable controller to buffer and add to form later + $rootScope.$$editableBuffer = $rootScope.$$editableBuffer || {}; + $rootScope.$$editableBuffer[attrs.eForm] = $rootScope.$$editableBuffer[attrs.eForm] || []; + $rootScope.$$editableBuffer[attrs.eForm].push(eCtrl); + scope.$form = null; //will be re-assigned later + } + // !hasForm + } else { + // create editableform controller + scope.$form = editableFormController(); + // add self to editable controller + scope.$form.$addEditable(eCtrl); + + // if `e-form` provided, publish local $form in scope + if(attrs.eForm) { + ($parse(attrs.eForm).assign || angular.noop)(scope.$parent, scope.$form); + } + + // bind click - if no external form defined + if(!attrs.eForm || attrs.eClickable) { + elem.addClass('editable-click'); + elem.bind(editableOptions.activationEvent, function(e) { + e.preventDefault(); + e.editable = eCtrl; + if(!is_disabled()) { + scope.$apply(function(){ + scope.$form.$show(); + }); + } + }); + } + } + + } + }; + }; +}]); + +/* +Returns editableForm controller +*/ +angular.module('xeditable').factory('editableFormController', + ['$parse', '$document', '$rootScope', 'editablePromiseCollection', 'editableUtils', + function($parse, $document, $rootScope, editablePromiseCollection, editableUtils) { + + // array of opened editable forms + var shown = []; + + //Check if the child element correspond or is a descendant of the parent element + var isSelfOrDescendant = function (parent, child) { + if (child == parent) { + return true; + } + + var node = child.parentNode; + while (node !== null) { + if (node == parent) { + return true; + } + node = node.parentNode; + } + return false; + }; + + //Check if it is a real blur : if the click event appear on a shown editable elem, this is not a blur. + var isBlur = function(shown, event) { + var isBlur = true; + + var editables = shown.$editables; + angular.forEach(editables, function(v){ + var element = v.editorEl[0]; + if (isSelfOrDescendant(element, event.target)) + isBlur = false; + + }); + return isBlur; + }; + + // bind click to body: cancel|submit|ignore forms + $document.bind('click', function(e) { + // ignore right/middle button click + if (e.which && e.which !== 1) { + return; + } + + var toCancel = []; + var toSubmit = []; + for (var i=0; i + * When trying to set the focus on a form field of a new row in the editable table, the `$activate` call needs to be wrapped in a `$timeout` call so that the form is rendered before the `$activate` function is called. + * + * @method $activate(name) + * @param {string} name name of field + * @memberOf editable-form + */ + $activate: function(name) { + var i, + selectionStart, + selectionEnd; + if (this.$editables.length) { + //activate by name + if (angular.isString(name)) { + for(i=0; i containing editable controls. + * It add some usefull methods to form variable exposed to scope by `name="myform"` attribute. + * + * @namespace editable-form + */ +angular.module('xeditable').directive('editableForm', + ['$rootScope', '$parse', 'editableFormController', 'editableOptions', + function($rootScope, $parse, editableFormController, editableOptions) { + return { + restrict: 'A', + require: ['form'], + //require: ['form', 'editableForm'], + //controller: EditableFormController, + compile: function() { + return { + pre: function(scope, elem, attrs, ctrl) { + var form = ctrl[0]; + var eForm; + + //if `editableForm` has value - publish smartly under this value + //this is required only for single editor form that is created and removed + if(attrs.editableForm) { + if(scope[attrs.editableForm] && scope[attrs.editableForm].$show) { + eForm = scope[attrs.editableForm]; + angular.extend(form, eForm); + } else { + eForm = editableFormController(); + scope[attrs.editableForm] = eForm; + angular.extend(eForm, form); + } + } else { //just merge to form and publish if form has name + eForm = editableFormController(); + angular.extend(form, eForm); + } + + //read editables from buffer (that appeared before FORM tag) + var buf = $rootScope.$$editableBuffer; + var name = form.$name; + if(name && buf && buf[name]) { + angular.forEach(buf[name], function(editable) { + eForm.$addEditable(editable); + }); + delete buf[name]; + } + }, + post: function(scope, elem, attrs, ctrl) { + var eForm; + + if(attrs.editableForm && scope[attrs.editableForm] && scope[attrs.editableForm].$show) { + eForm = scope[attrs.editableForm]; + } else { + eForm = ctrl[0]; + } + + /** + * Called when form is shown. + * + * @var {method|attribute} onshow + * @memberOf editable-form + */ + if(attrs.onshow) { + eForm.$onshow = angular.bind(eForm, $parse(attrs.onshow), scope); + } + + /** + * Called when form hides after both save or cancel. + * + * @var {method|attribute} onhide + * @memberOf editable-form + */ + if(attrs.onhide) { + eForm.$onhide = angular.bind(eForm, $parse(attrs.onhide), scope); + } + + /** + * Called when form is cancelled. + * + * @var {method|attribute} oncancel + * @memberOf editable-form + */ + if(attrs.oncancel) { + eForm.$oncancel = angular.bind(eForm, $parse(attrs.oncancel), scope); + } + + /** + * Whether form initially rendered in shown state. + * + * @var {bool|attribute} shown + * @memberOf editable-form + */ + if(attrs.shown && $parse(attrs.shown)(scope)) { + eForm.$show(); + } + + /** + * Action when form losses focus. Values: `cancel|submit|ignore`. + * Default is `ignore`. + * + * @var {string|attribute} blur + * @memberOf editable-form + */ + eForm._blur = attrs.blur || editableOptions.blurForm; + + // onbeforesave, onaftersave + if(!attrs.ngSubmit && !attrs.submit) { + /** + * Called after all children `onbeforesave` callbacks but before saving form values + * to model. + * If at least one children callback returns `non-string` - it will not not be called. + * See [editable-form demo](#editable-form) for details. + * + * @var {method|attribute} onbeforesave + * @memberOf editable-form + * + */ + if(attrs.onbeforesave) { + eForm.$onbeforesave = function() { + return $parse(attrs.onbeforesave)(scope, {$data: eForm.$data}); + }; + } + + /** + * Called when form values are saved to model. + * See [editable-form demo](#editable-form) for details. + * + * @var {method|attribute} onaftersave + * @memberOf editable-form + * + */ + if(attrs.onaftersave) { + eForm.$onaftersave = function() { + return $parse(attrs.onaftersave)(scope, {$data: eForm.$data}); + }; + } + + elem.bind('submit', function(event) { + event.preventDefault(); + scope.$apply(function() { + eForm.$submit(); + }); + }); + } + + + // click - mark form as clicked to exclude in document click handler + elem.bind('click', function(e) { + // ignore right/middle button click + if (e.which && e.which !== 1) { + return; + } + + if (eForm.$visible) { + eForm._clicked = true; + } + }); + + } + }; + } + }; +}]); +/** + * editablePromiseCollection + * + * Collect results of function calls. Shows waiting if there are promises. + * Finally, applies callbacks if: + * - onTrue(): all results are true and all promises resolved to true + * - onFalse(): at least one result is false or promise resolved to false + * - onString(): at least one result is string or promise rejected or promise resolved to string + */ + +angular.module('xeditable').factory('editablePromiseCollection', ['$q', function($q) { + + function promiseCollection() { + return { + promises: [], + hasFalse: false, + hasString: false, + when: function(result, noPromise) { + if (result === false) { + this.hasFalse = true; + } else if (!noPromise && angular.isObject(result)) { + this.promises.push($q.when(result)); + } else if (angular.isString(result)){ + this.hasString = true; + } else { //result === true || result === undefined || result === null + return; + } + }, + //callbacks: onTrue, onFalse, onString + then: function(callbacks) { + callbacks = callbacks || {}; + var onTrue = callbacks.onTrue || angular.noop; + var onFalse = callbacks.onFalse || angular.noop; + var onString = callbacks.onString || angular.noop; + var onWait = callbacks.onWait || angular.noop; + + var self = this; + + if (this.promises.length) { + onWait(true); + $q.all(this.promises).then( + //all resolved + function(results) { + onWait(false); + //check all results via same `when` method (without checking promises) + angular.forEach(results, function(result) { + self.when(result, true); + }); + applyCallback(); + }, + //some rejected + function(error) { + onWait(false); + onString(); + } + ); + } else { + applyCallback(); + } + + function applyCallback() { + if (!self.hasString && !self.hasFalse) { + onTrue(); + } else if (!self.hasString && self.hasFalse) { + onFalse(); + } else { + onString(); + } + } + + } + }; + } + + return promiseCollection; + +}]); + +/** + * editableUtils + */ + angular.module('xeditable').factory('editableUtils', [function() { + return { + indexOf: function (array, obj) { + if (array.indexOf) return array.indexOf(obj); + + for ( var i = 0; i < array.length; i++) { + if (obj === array[i]) return i; + } + return -1; + }, + + arrayRemove: function (array, value) { + var index = this.indexOf(array, value); + if (index >= 0) { + array.splice(index, 1); + } + return value; + }, + + // copy from https://github.com/angular/angular.js/blob/master/src/Angular.js + camelToDash: function(str) { + var SNAKE_CASE_REGEXP = /[A-Z]/g; + return str.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? '-' : '') + letter.toLowerCase(); + }); + }, + + dashToCamel: function(str) { + var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; + var MOZ_HACK_REGEXP = /^moz([A-Z])/; + return str. + replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }). + replace(MOZ_HACK_REGEXP, 'Moz$1'); + }, + + rename: function (tag, el) { + if (el[0] && el[0].attributes) { + var newEl = angular.element('<' + tag + '/>'); + newEl.html(el.html()); + var attrs = el[0].attributes; + for (var i = 0; i < attrs.length; ++i) { + newEl.attr(attrs.item(i).nodeName, attrs.item(i).value); + } + return newEl; + } + } + }; +}]); + +/** + * editableNgOptionsParser + * + * see: https://github.com/angular/angular.js/blob/master/src/ng/directive/select.js#L131 + */ + angular.module('xeditable').factory('editableNgOptionsParser', [function() { + //0000111110000000000022220000000000000000000000333300000000000000444444444444444000000000555555555555555000000066666666666666600000000000000007777000000000000000000088888 + var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/; + + function parser(optionsExp) { + var match; + + if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) { + throw 'ng-options parse error'; + } + + var + displayFn = match[2] || match[1], + valueName = match[4] || match[6], + keyName = match[5], + groupByFn = match[3] || '', + valueFn = match[2] ? match[1] : valueName, + valuesFn = match[7], + track = match[8], + trackFn = track ? match[8] : null; + + var ngRepeat; + if (keyName === undefined) { // array + ngRepeat = valueName + ' in ' + valuesFn; + if (track !== undefined) { + ngRepeat += ' track by '+trackFn; + } + } else { // object + ngRepeat = '('+keyName+', '+valueName+') in '+valuesFn; + } + + // group not supported yet + return { + ngRepeat: ngRepeat, + locals: { + valueName: valueName, + keyName: keyName, + valueFn: valueFn, + displayFn: displayFn + } + }; + } + + return parser; +}]); + +/** + * editableCombodate + * + * angular version of https://github.com/vitalets/combodate + */ +angular.module('xeditable').factory('editableCombodate', [function() { + function Combodate(element, options) { + this.$element = angular.element(element); + + if(this.$element[0].nodeName != 'INPUT') { + throw 'Combodate should be applied to INPUT element'; + } + + var currentYear = new Date().getFullYear(); + this.defaults = { + //in this format value stored in original input + format: 'YYYY-MM-DD HH:mm', + //in this format items in dropdowns are displayed + template: 'D / MMM / YYYY H : mm', + //initial value, can be `new Date()` + value: null, + minYear: 1970, + maxYear: currentYear, + yearDescending: true, + minuteStep: 5, + secondStep: 1, + firstItem: 'empty', //'name', 'empty', 'none' + errorClass: null, + customClass: '', + roundTime: true, // whether to round minutes and seconds if step > 1 + smartDays: true // whether days in combo depend on selected month: 31, 30, 28 + }; + + this.options = angular.extend({}, this.defaults, options); + this.init(); + } + + Combodate.prototype = { + constructor: Combodate, + init: function () { + this.map = { + //key regexp moment.method + day: ['D', 'date'], + month: ['M', 'month'], + year: ['Y', 'year'], + hour: ['[Hh]', 'hours'], + minute: ['m', 'minutes'], + second: ['s', 'seconds'], + ampm: ['[Aa]', ''] + }; + + this.$widget = angular.element('').html(this.getTemplate()); + + this.initCombos(); + + if (this.options.smartDays) { + var combo = this; + this.$widget.find('select').bind('change', function(e) { + // update days count if month or year changes + if (angular.element(e.target).hasClass('month') || angular.element(e.target).hasClass('year')) { + combo.fillCombo('day'); + } + }); + } + + this.$widget.find('select').css('width', 'auto'); + + // hide original input and insert widget + this.$element.css('display', 'none').after(this.$widget); + + // set initial value + this.setValue(this.$element.val() || this.options.value); + }, + + /* + Replace tokens in template with '); + }); + + return tpl; + }, + + /* + Initialize combos that presents in template + */ + initCombos: function() { + for (var k in this.map) { + var c = this.$widget[0].querySelectorAll('.'+k); + // set properties like this.$day, this.$month etc. + this['$'+k] = c.length ? angular.element(c) : null; + // fill with items + this.fillCombo(k); + } + }, + + /* + Fill combo with items + */ + fillCombo: function(k) { + var $combo = this['$'+k]; + if (!$combo) { + return; + } + + // define method name to fill items, e.g `fillDays` + var f = 'fill' + k.charAt(0).toUpperCase() + k.slice(1); + var items = this[f](); + var value = $combo.val(); + + $combo.html(''); + for(var i=0; i'+items[i][1]+''); + } + + $combo.val(value); + }, + + /* + Initialize items of combos. Handles `firstItem` option + */ + fillCommon: function(key) { + var values = [], relTime; + + if(this.options.firstItem === 'name') { + //need both to support moment ver < 2 and >= 2 + relTime = moment.relativeTime || moment.langData()._relativeTime; + var header = typeof relTime[key] === 'function' ? relTime[key](1, true, key, false) : relTime[key]; + //take last entry (see momentjs lang files structure) + header = header.split(' ').reverse()[0]; + values.push(['', header]); + } else if(this.options.firstItem === 'empty') { + values.push(['', '']); + } + return values; + }, + + + /* + fill day + */ + fillDay: function() { + var items = this.fillCommon('d'), name, i, + twoDigit = this.options.template.indexOf('DD') !== -1, + daysCount = 31; + + // detect days count (depends on month and year) + // originally https://github.com/vitalets/combodate/pull/7 + if (this.options.smartDays && this.$month && this.$year) { + var month = parseInt(this.$month.val(), 10); + var year = parseInt(this.$year.val(), 10); + + if (!isNaN(month) && !isNaN(year)) { + daysCount = moment([year, month]).daysInMonth(); + } + } + + for (i = 1; i <= daysCount; i++) { + name = twoDigit ? this.leadZero(i) : i; + items.push([i, name]); + } + return items; + }, + + /* + fill month + */ + fillMonth: function() { + var items = this.fillCommon('M'), name, i, + longNames = this.options.template.indexOf('MMMM') !== -1, + shortNames = this.options.template.indexOf('MMM') !== -1, + twoDigit = this.options.template.indexOf('MM') !== -1; + + for(i=0; i<=11; i++) { + if(longNames) { + //see https://github.com/timrwood/momentjs.com/pull/36 + name = moment().date(1).month(i).format('MMMM'); + } else if(shortNames) { + name = moment().date(1).month(i).format('MMM'); + } else if(twoDigit) { + name = this.leadZero(i+1); + } else { + name = i+1; + } + items.push([i, name]); + } + return items; + }, + + /* + fill year + */ + fillYear: function() { + var items = [], name, i, + longNames = this.options.template.indexOf('YYYY') !== -1; + + for(i=this.options.maxYear; i>=this.options.minYear; i--) { + name = longNames ? i : (i+'').substring(2); + items[this.options.yearDescending ? 'push' : 'unshift']([i, name]); + } + + items = this.fillCommon('y').concat(items); + + return items; + }, + + /* + fill hour + */ + fillHour: function() { + var items = this.fillCommon('h'), name, i, + h12 = this.options.template.indexOf('h') !== -1, + h24 = this.options.template.indexOf('H') !== -1, + twoDigit = this.options.template.toLowerCase().indexOf('hh') !== -1, + min = h12 ? 1 : 0, + max = h12 ? 12 : 23; + + for(i=min; i<=max; i++) { + name = twoDigit ? this.leadZero(i) : i; + items.push([i, name]); + } + return items; + }, + + /* + fill minute + */ + fillMinute: function() { + var items = this.fillCommon('m'), name, i, + twoDigit = this.options.template.indexOf('mm') !== -1; + + for(i=0; i<=59; i+= this.options.minuteStep) { + name = twoDigit ? this.leadZero(i) : i; + items.push([i, name]); + } + return items; + }, + + /* + fill second + */ + fillSecond: function() { + var items = this.fillCommon('s'), name, i, + twoDigit = this.options.template.indexOf('ss') !== -1; + + for(i=0; i<=59; i+= this.options.secondStep) { + name = twoDigit ? this.leadZero(i) : i; + items.push([i, name]); + } + return items; + }, + + /* + fill ampm + */ + fillAmpm: function() { + var ampmL = this.options.template.indexOf('a') !== -1, + ampmU = this.options.template.indexOf('A') !== -1, + items = [ + ['am', ampmL ? 'am' : 'AM'], + ['pm', ampmL ? 'pm' : 'PM'] + ]; + return items; + }, + + /* + Returns current date value from combos. + If format not specified - `options.format` used. + If format = `null` - Moment object returned. + */ + getValue: function(format) { + var dt, values = {}, + that = this, + notSelected = false; + + //getting selected values + angular.forEach(this.map, function(v, k) { + if(k === 'ampm') { + return; + } + var def = k === 'day' ? 1 : 0; + + values[k] = that['$'+k] ? parseInt(that['$'+k].val(), 10) : def; + + if(isNaN(values[k])) { + notSelected = true; + return false; + } + }); + + //if at least one visible combo not selected - return empty string + if(notSelected) { + return ''; + } + + //convert hours 12h --> 24h + if(this.$ampm) { + //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day) + if(values.hour === 12) { + values.hour = this.$ampm.val() === 'am' ? 0 : 12; + } else { + values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12; + } + } + + dt = moment([values.year, values.month, values.day, values.hour, values.minute, values.second]); + + //highlight invalid date + this.highlight(dt); + + format = format === undefined ? this.options.format : format; + if(format === null) { + return dt.isValid() ? dt : null; + } else { + return dt.isValid() ? dt.format(format) : ''; + } + }, + + setValue: function(value) { + if(!value) { + return; + } + + // parse in strict mode (third param `true`) + var dt = typeof value === 'string' ? moment(value, this.options.format, true) : moment(value), + that = this, + values = {}; + + //function to find nearest value in select options + function getNearest($select, value) { + var delta = {}; + angular.forEach($select.children('option'), function(opt, i){ + var optValue = angular.element(opt).attr('value'); + + if(optValue === '') return; + var distance = Math.abs(optValue - value); + if(typeof delta.distance === 'undefined' || distance < delta.distance) { + delta = {value: optValue, distance: distance}; + } + }); + return delta.value; + } + + if(dt.isValid()) { + //read values from date object + angular.forEach(this.map, function(v, k) { + if(k === 'ampm') { + return; + } + values[k] = dt[v[1]](); + }); + + if(this.$ampm) { + //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day) + if(values.hour >= 12) { + values.ampm = 'pm'; + if(values.hour > 12) { + values.hour -= 12; + } + } else { + values.ampm = 'am'; + if(values.hour === 0) { + values.hour = 12; + } + } + } + + angular.forEach(values, function(v, k) { + //call val() for each existing combo, e.g. this.$hour.val() + if(that['$'+k]) { + + if(k === 'minute' && that.options.minuteStep > 1 && that.options.roundTime) { + v = getNearest(that['$'+k], v); + } + + if(k === 'second' && that.options.secondStep > 1 && that.options.roundTime) { + v = getNearest(that['$'+k], v); + } + + that['$'+k].val(v); + } + }); + + // update days count + if (this.options.smartDays) { + this.fillCombo('day'); + } + + this.$element.val(dt.format(this.options.format)).triggerHandler('change'); + } + }, + + /* + highlight combos if date is invalid + */ + highlight: function(dt) { + if(!dt.isValid()) { + if(this.options.errorClass) { + this.$widget.addClass(this.options.errorClass); + } else { + //store original border color + if(!this.borderColor) { + this.borderColor = this.$widget.find('select').css('border-color'); + } + this.$widget.find('select').css('border-color', 'red'); + } + } else { + if(this.options.errorClass) { + this.$widget.removeClass(this.options.errorClass); + } else { + this.$widget.find('select').css('border-color', this.borderColor); + } + } + }, + + leadZero: function(v) { + return v <= 9 ? '0' + v : v; + }, + + destroy: function() { + this.$widget.remove(); + this.$element.removeData('combodate').show(); + } + + }; + + return { + getInstance: function(element, options) { + return new Combodate(element, options); + } + }; +}]); + +/* +Editable icons: +- default +- font-awesome + +*/ +angular.module('xeditable').factory('editableIcons', function() { + + var icons = { + //Icon-set to use, defaults to bootstrap icons + default: { + 'bs2': { + ok: 'icon-ok icon-white', + cancel: 'icon-remove', + clear: 'icon-trash' + }, + 'bs3': { + ok: 'glyphicon glyphicon-ok', + cancel: 'glyphicon glyphicon-remove', + clear: 'glyphicon glyphicon-trash' + } + }, + external: { + 'font-awesome': { + ok: 'fa fa-check', + cancel: 'fa fa-times', + clear: 'fa fa-trash' + } + } + }; + + return icons; +}); + +/* jshint -W086 */ +/* +Editable themes: +- default +- bootstrap 2 +- bootstrap 3 +- semantic-ui + +Note: in postrender() `this` is instance of editableController +*/ +angular.module('xeditable').factory('editableThemes', function() { + var themes = { + //default + 'default': { + formTpl: '', + noformTpl: '', + controlsTpl: '', + inputTpl: '', + errorTpl: '
', + buttonsTpl: '', + submitTpl: '', + cancelTpl: '', + resetTpl: '' + }, + + //bs2 + 'bs2': { + formTpl: '
', + noformTpl: '', + controlsTpl: '
', + inputTpl: '', + errorTpl: '
', + buttonsTpl: '', + submitTpl: '', + cancelTpl: '', + resetTpl: '' + + }, + + //bs3 + 'bs3': { + formTpl: '
', + noformTpl: '', + controlsTpl: '
', + inputTpl: '', + errorTpl: '
', + buttonsTpl: '', + submitTpl: '', + cancelTpl: '', + resetTpl: '', + + //bs3 specific prop to change buttons class: btn-sm, btn-lg + buttonsClass: '', + //bs3 specific prop to change standard inputs class: input-sm, input-lg + inputClass: '', + postrender: function() { + //apply `form-control` class to std inputs + switch(this.directiveName) { + case 'editableText': + case 'editableSelect': + case 'editableTextarea': + case 'editableEmail': + case 'editableTel': + case 'editableNumber': + case 'editableUrl': + case 'editableSearch': + case 'editableDate': + case 'editableDatetime': + case 'editableBsdate': + case 'editableTime': + case 'editableMonth': + case 'editableWeek': + case 'editablePassword': + case 'editableDatetimeLocal': + this.inputEl.addClass('form-control'); + if(this.theme.inputClass) { + // don`t apply `input-sm` and `input-lg` to select multiple + // should be fixed in bs itself! + if(this.inputEl.attr('multiple') && + (this.theme.inputClass === 'input-sm' || this.theme.inputClass === 'input-lg')) { + break; + } + this.inputEl.addClass(this.theme.inputClass); + } + break; + case 'editableCheckbox': + this.editorEl.addClass('checkbox'); + } + + //apply buttonsClass (bs3 specific!) + if(this.buttonsEl && this.theme.buttonsClass) { + this.buttonsEl.find('button').addClass(this.theme.buttonsClass); + } + } + }, + + //semantic-ui + 'semantic': { + formTpl: '
', + noformTpl: '', + controlsTpl: '
', + inputTpl: '', + errorTpl: '
', + buttonsTpl: '', + submitTpl: '', + cancelTpl: '', + resetTpl: '' + } + }; + + return themes; +}); diff --git a/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/js/xeditable.min.js b/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/js/xeditable.min.js new file mode 100755 index 0000000..1c06f95 --- /dev/null +++ b/3rd_party/static/testapi-ui/assets/lib/angular-xeditable-0.8.0/js/xeditable.min.js @@ -0,0 +1,7 @@ +/*! +angular-xeditable - 0.8.0 +Edit-in-place for angular.js +Build date: 2017-06-06 +*/ +angular.module("xeditable",[]).value("editableOptions",{theme:"default",icon_set:"default",buttons:"right",blurElem:"cancel",blurForm:"ignore",activate:"focus",isDisabled:!1,activationEvent:"click",submitButtonTitle:"Submit",submitButtonAriaLabel:"Submit",cancelButtonTitle:"Cancel",cancelButtonAriaLabel:"Cancel",clearButtonTitle:"Clear",clearButtonAriaLabel:"Clear",displayClearButton:!1}),angular.module("xeditable").directive("editableBsdate",["editableDirectiveFactory","$injector","$parse",function(a,b,c){uibDatepickerConfig=b.get("uibDatepickerConfig"),uibDatepickerPopupConfig=b.get("uibDatepickerPopupConfig");var d=[["eIsOpen","is-open"],["eDateDisabled","date-disabled"],["eDatepickerPopup","uib-datepicker-popup"],["eShowButtonBar","show-button-bar"],["eCurrentText","current-text"],["eClearText","clear-text"],["eCloseText","close-text"],["eCloseOnDateSelection","close-on-date-selection"],["eDatepickerAppendToBody","datepicker-append-to-body"],["eOnOpenFocus","on-open-focus"],["eName","name"],["eDateDisabled","date-disabled"],["eAltInputFormats","alt-input-formats"]],e=[["eFormatDay","formatDay"],["eFormatMonth","formatMonth"],["eFormatYear","formatYear"],["eFormatDayHeader","formatDayHeader"],["eFormatDayTitle","formatDayTitle"],["eFormatMonthTitle","formatMonthTitle"],["eMaxMode","maxMode"],["eMinMode","minMode"],["eDatepickerMode","datepickerMode"]];return a({directiveName:"editableBsdate",inputTpl:"
",render:function(){this.parent.render.call(this);var a=this.attrs,b=this.scope,f=angular.element('');f.attr("uib-datepicker-popup",a.eDatepickerPopupXEditable||uibDatepickerPopupConfig.datepickerPopup),f.attr("year-range",a.eYearRange||20),f.attr("ng-readonly",a.eReadonly||!1);for(var g=d.length-1;g>=0;g--){var h=a[d[g][0]];"undefined"!=typeof h&&f.attr(d[g][1],h)}a.eNgChange&&(f.attr("ng-change",a.eNgChange),this.inputEl.removeAttr("ng-change")),a.eStyle&&(f.attr("style",a.eStyle),this.inputEl.removeAttr("style"));var i={maxDate:b.$eval(a.eMaxDate)||uibDatepickerConfig.maxDate,minDate:b.$eval(a.eMinDate)||uibDatepickerConfig.minDate,showWeeks:a.eShowWeeks?"true"===a.eShowWeeks.toLowerCase():uibDatepickerConfig.showWeeks,startingDay:a.eStartingDay||0,initDate:b.$eval(a.eInitDate)||new Date};if(a.eDatepickerOptions){var j=c(a.eDatepickerOptions)(b);angular.extend(i,j)}for(var k=e.length-1;k>=0;k--){var l=a[e[k][0]];"undefined"!=typeof l&&(i[e[k][1]]=l)}b.dateOptions=i;var m=angular.isDefined(a.eShowCalendarButton)?a.eShowCalendarButton:"true";if("true"===m){var n=angular.element(''),o=angular.element('');n.attr("ng-click",a.eNgClick),o.append(n),this.inputEl.append(o)}else f.attr("ng-click",a.eNgClick);f.attr("datepicker-options","dateOptions"),this.inputEl.prepend(f),this.inputEl.removeAttr("class"),this.inputEl.removeAttr("ng-click"),this.inputEl.removeAttr("is-open"),this.inputEl.removeAttr("init-date"),this.inputEl.removeAttr("datepicker-popup"),this.inputEl.removeAttr("required"),this.inputEl.removeAttr("ng-model"),this.inputEl.removeAttr("date-picker-append-to-body"),this.inputEl.removeAttr("name"),this.inputEl.attr("class","input-group")},autosubmit:function(){var a=this;a.inputEl.bind("change",function(){setTimeout(function(){a.scope.$apply(function(){a.scope.$form.$submit()})},500)}),a.inputEl.bind("keydown",function(b){9===b.keyCode&&"submit"===a.editorEl.attr("blur")&&a.scope.$apply(function(){a.scope.$form.$submit()})})}})}]),angular.module("xeditable").directive("editableBstime",["editableDirectiveFactory",function(a){return a({directiveName:"editableBstime",inputTpl:"
",render:function(){this.parent.render.call(this);var a=angular.element('
');a.attr("ng-model",this.inputEl.attr("ng-model")),this.inputEl.removeAttr("ng-model"),this.attrs.eNgChange&&(a.attr("ng-change",this.inputEl.attr("ng-change")),this.inputEl.removeAttr("ng-change")),this.inputEl.wrap(a)}})}]),angular.module("xeditable").directive("editableCheckbox",["editableDirectiveFactory",function(a){return a({directiveName:"editableCheckbox",inputTpl:'',render:function(){this.parent.render.call(this),this.inputEl.wrap(""),this.attrs.eTitle&&this.inputEl.parent().append(""+this.attrs.eTitle+"")},autosubmit:function(){var a=this;a.inputEl.bind("change",function(){setTimeout(function(){a.scope.$apply(function(){a.scope.$form.$submit()})},500)})}})}]),angular.module("xeditable").directive("editableChecklist",["editableDirectiveFactory","editableNgOptionsParser",function(a,b){return a({directiveName:"editableChecklist",inputTpl:"",useCopy:!0,render:function(){this.parent.render.call(this);var a=b(this.attrs.eNgOptions),c="",d="";this.attrs.eNgChange&&(c=' ng-change="'+this.attrs.eNgChange+'"'),this.attrs.eChecklistComparator&&(d=' checklist-comparator="'+this.attrs.eChecklistComparator+'"');var e='';this.inputEl.removeAttr("ng-model"),this.inputEl.removeAttr("ng-options"),this.inputEl.removeAttr("ng-change"),this.inputEl.removeAttr("checklist-comparator"),this.inputEl.html(e)}})}]),angular.module("xeditable").directive("editableCombodate",["editableDirectiveFactory","editableCombodate",function(a,b){return a({directiveName:"editableCombodate",inputTpl:'',render:function(){this.parent.render.call(this);var a={value:new Date(this.scope.$data)},c=this;angular.forEach(["format","template","minYear","maxYear","yearDescending","minuteStep","secondStep","firstItem","errorClass","customClass","roundTime","smartDays"],function(b){var d="e"+b.charAt(0).toUpperCase()+b.slice(1);d in c.attrs&&("minYear"==b||"maxYear"==b||"minuteStep"==b||"secondStep"==b?a[b]=parseInt(c.attrs[d],10):a[b]=c.attrs[d])});var d=b.getInstance(this.inputEl,a);d.$widget.find("select").bind("change",function(a){c.scope.$data=d.getValue()?new Date(d.getValue().replace(/-/g,"/")).toISOString():null})}})}]),function(){var a=function(a){return a.toLowerCase().replace(/-(.)/g,function(a,b){return b.toUpperCase()})},b="text|password|email|tel|number|url|search|color|date|datetime|datetime-local|time|month|week|file".split("|");angular.forEach(b,function(b){var c=a("editable-"+b);angular.module("xeditable").directive(c,["editableDirectiveFactory",function(a){return a({directiveName:c,inputTpl:'',render:function(){if(this.parent.render.call(this),this.attrs.eInputgroupleft||this.attrs.eInputgroupright){if(this.inputEl.wrap('
'),this.attrs.eInputgroupleft){var a=angular.element(''+this.attrs.eInputgroupleft+"");this.inputEl.parent().prepend(a)}if(this.attrs.eInputgroupright){var b=angular.element(''+this.attrs.eInputgroupright+"");this.inputEl.parent().append(b)}}if(this.attrs.eLabel){var c=angular.element("");this.attrs.eInputgroupleft||this.attrs.eInputgroupright?this.inputEl.parent().parent().prepend(c):this.inputEl.parent().prepend(c)}this.attrs.eFormclass&&this.editorEl.addClass(this.attrs.eFormclass)},autosubmit:function(){var a=this;a.inputEl.bind("keydown",function(b){9===b.keyCode&&"submit"===a.editorEl.attr("blur")&&a.scope.$apply(function(){a.scope.$form.$submit()})})}})}])}),angular.module("xeditable").directive("editableRange",["editableDirectiveFactory","$interpolate",function(a,b){return a({directiveName:"editableRange",inputTpl:'',render:function(){this.parent.render.call(this),this.inputEl.after(""+b.startSymbol()+"$data"+b.endSymbol()+"")}})}])}(),angular.module("xeditable").directive("editableTagsInput",["editableDirectiveFactory","editableUtils",function(a,b){var c=a({directiveName:"editableTagsInput",inputTpl:"",useCopy:!0,render:function(){this.parent.render.call(this),this.inputEl.append(b.rename("auto-complete",this.attrs.$autoCompleteElement)),this.inputEl.removeAttr("ng-model"),this.inputEl.attr("ng-model","$parent.$data")}}),d=c.link;return c.link=function(a,b,c,e){var f=b.find("editable-tags-input-auto-complete");return c.$autoCompleteElement=f.clone(),f.remove(),d(a,b,c,e)},c}]),angular.module("xeditable").directive("editableRadiolist",["editableDirectiveFactory","editableNgOptionsParser","$interpolate",function(a,b,c){return a({directiveName:"editableRadiolist",inputTpl:"",render:function(){this.parent.render.call(this);var a=b(this.attrs.eNgOptions),d="",e="";this.attrs.eNgChange&&(d=' ng-change="'+this.attrs.eNgChange+'"'),this.attrs.eName&&(e=' name="'+this.attrs.eName+'"');var f='';this.inputEl.removeAttr("ng-model"),this.inputEl.removeAttr("ng-options"),this.inputEl.removeAttr("ng-change"),this.inputEl.html(f)},autosubmit:function(){var a=this;a.inputEl.bind("change",function(){setTimeout(function(){a.scope.$apply(function(){a.scope.$form.$submit()})},500)})}})}]),angular.module("xeditable").directive("editableSelect",["editableDirectiveFactory",function(a){return a({directiveName:"editableSelect",inputTpl:"",render:function(){if(this.parent.render.call(this),this.attrs.ePlaceholder){var a=angular.element('");this.inputEl.append(a)}},autosubmit:function(){var a=this;a.attrs.hasOwnProperty("eMultiple")||a.inputEl.bind("change",function(){a.scope.$apply(function(){a.scope.$form.$submit()})})}})}]),angular.module("xeditable").directive("editableTextarea",["editableDirectiveFactory",function(a){return a({directiveName:"editableTextarea",inputTpl:"",addListeners:function(){var a=this;a.parent.addListeners.call(a),a.single&&"no"!==a.buttons&&a.autosubmit()},autosubmit:function(){var a=this;a.inputEl.bind("keydown",function(b){a.attrs.submitOnEnter?13!==b.keyCode||b.shiftKey||a.scope.$apply(function(){a.scope.$form.$submit()}):((b.ctrlKey||b.metaKey)&&13===b.keyCode||9===b.keyCode&&"submit"===a.editorEl.attr("blur"))&&a.scope.$apply(function(){a.scope.$form.$submit()})})}})}]),angular.module("xeditable").directive("editableUidate",["editableDirectiveFactory",function(a){return a({directiveName:"editableUidate",inputTpl:'',render:function(){this.parent.render.call(this),this.inputEl.attr("ui-date",this.attrs.eUiDate),this.inputEl.attr("placeholder",this.attrs.ePlaceholder)}})}]),angular.module("xeditable").directive("editableUiSelect",["editableDirectiveFactory","editableUtils",function(a,b){var c=a({directiveName:"editableUiSelect",inputTpl:"",render:function(){this.parent.render.call(this),this.inputEl.append(b.rename("ui-select-match",this.attrs.$matchElement)),this.inputEl.append(b.rename("ui-select-choices",this.attrs.$choicesElement)),this.inputEl.removeAttr("ng-model"),this.inputEl.attr("ng-model","$parent.$parent.$data")},autosubmit:function(){var a=this;a.inputEl.bind("change",function(){setTimeout(function(){a.scope.$apply(function(){a.scope.$form.$submit()})},500)}),a.inputEl.bind("keydown",function(b){9===b.keyCode&&"submit"===a.editorEl.attr("blur")&&a.scope.$apply(function(){a.scope.$form.$submit()})})}}),d=c.link;return c.link=function(a,b,c,e){var f=b.find("editable-ui-select-match"),g=b.find("editable-ui-select-choices");return c.$matchElement=f.clone(),c.$choicesElement=g.clone(),f.remove(),g.remove(),d(a,b,c,e)},c}]),angular.module("xeditable").factory("editableController",["$q","editableUtils",function(a,b){function c(a,c,d,e,f,g,h,i,j,k,l,m){var n,o,p=this;p.scope=a,p.elem=d,p.attrs=c,p.inputEl=null,p.editorEl=null,p.single=!0,p.error="",p.theme=f[c.editableTheme]||f[h.theme]||f["default"],p.parent={};var q=c.editableTheme||h.theme||"default",r=c.editableIconSet||h.icon_set;p.icon_set="default"===r?g["default"][q]:g.external[r],p.inputTpl="",p.directiveName="",p.useCopy=!1,p.single=null,p.buttons="right",p.popover=!1,p.init=function(b){if(p.single=b,p.name=c.eName||c[p.directiveName],!c[p.directiveName])throw"You should provide value for `"+p.directiveName+"` in editable element!";n=e(c[p.directiveName]),p.single?p.buttons=p.attrs.buttons||h.buttons:p.buttons="no",c.eName&&p.scope.$watch("$data",function(a){p.scope.$form.$data[c.eName]=a}),c.onshow&&(p.onshow=function(){return p.catchError(e(c.onshow)(a))}),c.onhide&&(p.onhide=function(){return e(c.onhide)(a)}),c.oncancel&&(p.oncancel=function(){return e(c.oncancel)(a)}),c.onbeforesave&&(p.onbeforesave=function(){return p.catchError(e(c.onbeforesave)(a))}),c.onaftersave&&(p.onaftersave=function(){return p.catchError(e(c.onaftersave)(a))}),c.popover&&(p.popover=p.attrs.popover),a.$parent.$watch(c[p.directiveName],function(a,b){p.setLocalValue(),p.handleEmpty()})},p.render=function(){var a=p.theme;p.inputEl=angular.element(p.inputTpl),p.controlsEl=angular.element(a.controlsTpl),p.controlsEl.append(p.inputEl),"no"!==p.buttons&&(p.buttonsEl=angular.element(a.buttonsTpl),p.submitEl=angular.element(a.submitTpl),p.resetEl=angular.element(a.resetTpl),p.cancelEl=angular.element(a.cancelTpl),p.submitEl.attr("title",h.submitButtonTitle),p.submitEl.attr("aria-label",h.submitButtonAriaLabel),p.cancelEl.attr("title",h.cancelButtonTitle),p.cancelEl.attr("aria-label",h.cancelButtonAriaLabel),p.resetEl.attr("title",h.clearButtonTitle),p.resetEl.attr("aria-label",h.clearButtonAriaLabel),p.icon_set&&(p.submitEl.find("span").addClass(p.icon_set.ok),p.cancelEl.find("span").addClass(p.icon_set.cancel),p.resetEl.find("span").addClass(p.icon_set.clear)),p.buttonsEl.append(p.submitEl).append(p.cancelEl),h.displayClearButton&&p.buttonsEl.append(p.resetEl),p.controlsEl.append(p.buttonsEl),p.inputEl.addClass("editable-has-buttons")),p.errorEl=angular.element(a.errorTpl),p.controlsEl.append(p.errorEl),p.editorEl=angular.element(p.single?a.formTpl:a.noformTpl),p.editorEl.append(p.controlsEl);for(var d in c.$attr)if(!(d.length<=1)){var e=!1,f=d.substring(1,2);if("e"===d.substring(0,1)&&f===f.toUpperCase()&&(e=d.substring(1),"Form"!==e&&"NgSubmit"!==e)){var g=e.substring(0,1),i=e.substring(1,2);e=i===i.toUpperCase()&&g===g.toUpperCase()?g.toLowerCase()+"-"+b.camelToDash(e.substring(1)):g.toLowerCase()+b.camelToDash(e.substring(1));var j="value"!==e&&""===c[d]?e:c[d];p.inputEl.attr(e,j)}}if(p.inputEl.addClass("editable-input"),p.inputEl.attr("ng-model","$parent.$data"),p.editorEl.addClass(b.camelToDash(p.directiveName)),p.single&&(p.editorEl.attr("editable-form","$form"),p.editorEl.attr("blur",p.attrs.blur||h.blurElem)),p.popover){var k=angular.element("
");k.append(p.editorEl),p.editorEl=k,m.put("popover.html",p.editorEl[0].outerHTML)}angular.isFunction(a.postrender)&&a.postrender.call(p)},p.setLocalValue=function(){p.scope.$data=p.useCopy?angular.copy(n(a.$parent)):n(a.$parent)};var s=null;p.show=function(){return p.setLocalValue(),p.render(),d.after(p.editorEl),s=a.$new(),j(p.editorEl)(s),p.addListeners(),d.addClass("editable-hide"),p.onshow()},p.hide=function(){return s.$destroy(),p.controlsEl.remove(),p.editorEl.remove(),d.removeClass("editable-hide"),p.popover&&m.remove("popover.html"),p.onhide()},p.cancel=function(){p.oncancel()},p.addListeners=function(){p.inputEl.bind("keyup",function(a){if(p.single)switch(a.keyCode){case 27:p.scope.$apply(function(){p.scope.$form.$cancel()})}}),p.single&&"no"===p.buttons&&p.autosubmit(),p.editorEl.bind("click",function(a){a.which&&1!==a.which||p.scope.$form.$visible&&(p.scope.$form._clicked=!0)})},p.setWaiting=function(a){a?(o=!p.inputEl.attr("disabled")&&!p.inputEl.attr("ng-disabled")&&!p.inputEl.attr("ng-enabled"),o&&(p.inputEl.attr("disabled","disabled"),p.buttonsEl&&p.buttonsEl.find("button").attr("disabled","disabled"))):o&&(p.inputEl.removeAttr("disabled"),p.buttonsEl&&p.buttonsEl.find("button").removeAttr("disabled"))},p.activate=function(a,b){setTimeout(function(){var c=p.inputEl[0];"focus"===h.activate&&c.focus?(void 0!==a&&""!==a&&c.setSelectionRange&&(b=b||a,c.onfocus=function(){setTimeout(function(){try{this.setSelectionRange(a,b)}catch(c){}}.bind(this))}),"editableRadiolist"==p.directiveName||"editableChecklist"==p.directiveName||"editableBsdate"==p.directiveName||"editableTagsInput"==p.directiveName?c.querySelector(".ng-pristine").focus():c.focus()):"select"===h.activate&&(c.select?c.select():c.focus&&c.focus())},0)},p.setError=function(b){angular.isObject(b)||(a.$error=l.trustAsHtml(b),p.error=b)},p.catchError=function(a,b){return angular.isObject(a)&&b!==!0?k.when(a).then(angular.bind(this,function(a){this.catchError(a,!0)}),angular.bind(this,function(a){this.catchError(a,!0)})):b&&angular.isObject(a)&&a.status&&200!==a.status&&a.data&&angular.isString(a.data)?(this.setError(a.data),a=a.data):angular.isString(a)&&this.setError(a),a},p.save=function(){n.assign(a.$parent,p.useCopy?angular.copy(p.scope.$data):p.scope.$data)},p.handleEmpty=function(){var b=n(a.$parent),c=null===b||void 0===b||""===b||angular.isArray(b)&&0===b.length;d.toggleClass("editable-empty",c)},p.autosubmit=angular.noop,p.onshow=angular.noop,p.onhide=angular.noop,p.oncancel=angular.noop,p.onbeforesave=angular.noop,p.onaftersave=angular.noop}return c.$inject=["$scope","$attrs","$element","$parse","editableThemes","editableIcons","editableOptions","$rootScope","$compile","$q","$sce","$templateCache"],c}]),angular.module("xeditable").factory("editableDirectiveFactory",["$parse","$compile","editableThemes","$rootScope","$document","editableController","editableFormController","editableOptions",function(a,b,c,d,e,f,g,h){return function(b){return{restrict:"A",scope:!0,require:[b.directiveName,"?^form"],controller:f,link:function(c,f,i,j){var k,l=j[0],m=!1;if(j[1])k=j[1],m=void 0===i.eSingle;else if(i.eForm){var n=a(i.eForm)(c);if(n)k=n,m=!0;else if(f&&"function"==typeof f.parents&&f.parents().last().find("form[name="+i.eForm+"]").length)k=null,m=!0;else for(var o=0;o=0&&a.splice(c,1),b},camelToDash:function(a){var b=/[A-Z]/g;return a.replace(b,function(a,b){return(b?"-":"")+a.toLowerCase()})},dashToCamel:function(a){var b=/([\:\-\_]+(.))/g,c=/^moz([A-Z])/;return a.replace(b,function(a,b,c,d){return d?c.toUpperCase():c}).replace(c,"Moz$1")},rename:function(a,b){if(b[0]&&b[0].attributes){var c=angular.element("<"+a+"/>");c.html(b.html());for(var d=b[0].attributes,e=0;e
').html(this.getTemplate()),this.initCombos(),this.options.smartDays){var a=this;this.$widget.find("select").bind("change",function(b){(angular.element(b.target).hasClass("month")||angular.element(b.target).hasClass("year"))&&a.fillCombo("day")})}this.$widget.find("select").css("width","auto"),this.$element.css("display","none").after(this.$widget),this.setValue(this.$element.val()||this.options.value)},getTemplate:function(){var a=this.options.template,b=this.options.customClass;return angular.forEach(this.map,function(b,c){b=b[0];var d=new RegExp(b+"+"),e=b.length>1?b.substring(1,2):b;a=a.replace(d,"{"+e+"}")}),a=a.replace(/ /g," "),angular.forEach(this.map,function(c,d){c=c[0];var e=c.length>1?c.substring(1,2):c;a=a.replace("{"+e+"}",'')}),a},initCombos:function(){for(var a in this.map){var b=this.$widget[0].querySelectorAll("."+a);this["$"+a]=b.length?angular.element(b):null,this.fillCombo(a)}},fillCombo:function(a){var b=this["$"+a];if(b){var c="fill"+a.charAt(0).toUpperCase()+a.slice(1),d=this[c](),e=b.val();b.html("");for(var f=0;f'+d[f][1]+"");b.val(e)}},fillCommon:function(a){var b,c=[];if("name"===this.options.firstItem){b=moment.relativeTime||moment.langData()._relativeTime;var d="function"==typeof b[a]?b[a](1,!0,a,!1):b[a];d=d.split(" ").reverse()[0],c.push(["",d])}else"empty"===this.options.firstItem&&c.push(["",""]);return c},fillDay:function(){var a,b,c=this.fillCommon("d"),d=-1!==this.options.template.indexOf("DD"),e=31;if(this.options.smartDays&&this.$month&&this.$year){var f=parseInt(this.$month.val(),10),g=parseInt(this.$year.val(),10);isNaN(f)||isNaN(g)||(e=moment([g,f]).daysInMonth())}for(b=1;e>=b;b++)a=d?this.leadZero(b):b,c.push([b,a]);return c},fillMonth:function(){var a,b,c=this.fillCommon("M"),d=-1!==this.options.template.indexOf("MMMM"),e=-1!==this.options.template.indexOf("MMM"),f=-1!==this.options.template.indexOf("MM");for(b=0;11>=b;b++)a=d?moment().date(1).month(b).format("MMMM"):e?moment().date(1).month(b).format("MMM"):f?this.leadZero(b+1):b+1,c.push([b,a]);return c},fillYear:function(){var a,b,c=[],d=-1!==this.options.template.indexOf("YYYY");for(b=this.options.maxYear;b>=this.options.minYear;b--)a=d?b:(b+"").substring(2),c[this.options.yearDescending?"push":"unshift"]([b,a]);return c=this.fillCommon("y").concat(c)},fillHour:function(){var a,b,c=this.fillCommon("h"),d=-1!==this.options.template.indexOf("h"),e=(-1!==this.options.template.indexOf("H"),-1!==this.options.template.toLowerCase().indexOf("hh")),f=d?1:0,g=d?12:23;for(b=f;g>=b;b++)a=e?this.leadZero(b):b,c.push([b,a]);return c},fillMinute:function(){var a,b,c=this.fillCommon("m"),d=-1!==this.options.template.indexOf("mm");for(b=0;59>=b;b+=this.options.minuteStep)a=d?this.leadZero(b):b,c.push([b,a]);return c},fillSecond:function(){var a,b,c=this.fillCommon("s"),d=-1!==this.options.template.indexOf("ss");for(b=0;59>=b;b+=this.options.secondStep)a=d?this.leadZero(b):b,c.push([b,a]);return c},fillAmpm:function(){var a=-1!==this.options.template.indexOf("a"),b=(-1!==this.options.template.indexOf("A"),[["am",a?"am":"AM"],["pm",a?"pm":"PM"]]);return b},getValue:function(a){var b,c={},d=this,e=!1;return angular.forEach(this.map,function(a,b){if("ampm"!==b){var f="day"===b?1:0;return c[b]=d["$"+b]?parseInt(d["$"+b].val(),10):f,isNaN(c[b])?(e=!0,!1):void 0}}),e?"":(this.$ampm&&(12===c.hour?c.hour="am"===this.$ampm.val()?0:12:c.hour="am"===this.$ampm.val()?c.hour:c.hour+12),b=moment([c.year,c.month,c.day,c.hour,c.minute,c.second]),this.highlight(b),a=void 0===a?this.options.format:a,null===a?b.isValid()?b:null:b.isValid()?b.format(a):"")},setValue:function(a){function b(a,b){var c={};return angular.forEach(a.children("option"),function(a,d){var e=angular.element(a).attr("value");if(""!==e){var f=Math.abs(e-b);("undefined"==typeof c.distance||f=12?(e.ampm="pm",e.hour>12&&(e.hour-=12)):(e.ampm="am",0===e.hour&&(e.hour=12))),angular.forEach(e,function(a,c){d["$"+c]&&("minute"===c&&d.options.minuteStep>1&&d.options.roundTime&&(a=b(d["$"+c],a)),"second"===c&&d.options.secondStep>1&&d.options.roundTime&&(a=b(d["$"+c],a)),d["$"+c].val(a))}),this.options.smartDays&&this.fillCombo("day"),this.$element.val(c.format(this.options.format)).triggerHandler("change")); +}},highlight:function(a){a.isValid()?this.options.errorClass?this.$widget.removeClass(this.options.errorClass):this.$widget.find("select").css("border-color",this.borderColor):this.options.errorClass?this.$widget.addClass(this.options.errorClass):(this.borderColor||(this.borderColor=this.$widget.find("select").css("border-color")),this.$widget.find("select").css("border-color","red"))},leadZero:function(a){return 9>=a?"0"+a:a},destroy:function(){this.$widget.remove(),this.$element.removeData("combodate").show()}},{getInstance:function(b,c){return new a(b,c)}}}]),angular.module("xeditable").factory("editableIcons",function(){var a={"default":{bs2:{ok:"icon-ok icon-white",cancel:"icon-remove",clear:"icon-trash"},bs3:{ok:"glyphicon glyphicon-ok",cancel:"glyphicon glyphicon-remove",clear:"glyphicon glyphicon-trash"}},external:{"font-awesome":{ok:"fa fa-check",cancel:"fa fa-times",clear:"fa fa-trash"}}};return a}),angular.module("xeditable").factory("editableThemes",function(){var a={"default":{formTpl:'
',noformTpl:'',controlsTpl:'',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:'',resetTpl:''},bs2:{formTpl:'
',noformTpl:'',controlsTpl:'
',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:'',resetTpl:''},bs3:{formTpl:'
',noformTpl:'',controlsTpl:'
',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:'',resetTpl:'',buttonsClass:"",inputClass:"",postrender:function(){switch(this.directiveName){case"editableText":case"editableSelect":case"editableTextarea":case"editableEmail":case"editableTel":case"editableNumber":case"editableUrl":case"editableSearch":case"editableDate":case"editableDatetime":case"editableBsdate":case"editableTime":case"editableMonth":case"editableWeek":case"editablePassword":case"editableDatetimeLocal":if(this.inputEl.addClass("form-control"),this.theme.inputClass){if(this.inputEl.attr("multiple")&&("input-sm"===this.theme.inputClass||"input-lg"===this.theme.inputClass))break;this.inputEl.addClass(this.theme.inputClass)}break;case"editableCheckbox":this.editorEl.addClass("checkbox")}this.buttonsEl&&this.theme.buttonsClass&&this.buttonsEl.find("button").addClass(this.theme.buttonsClass)}},semantic:{formTpl:'
',noformTpl:'',controlsTpl:'
',inputTpl:"",errorTpl:'
',buttonsTpl:'',submitTpl:'',cancelTpl:'',resetTpl:''}};return a}); \ No newline at end of file -- cgit 1.2.3-korg