mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* Deleted unused directives - alert - pagination - pager - bar - tooltipPopup - tooltipHtmlUnsafePopup
This commit is contained in:
parent
f09e17e76c
commit
20a3f0175c
23 changed files with 72 additions and 1844 deletions
26
NOTICE.txt
26
NOTICE.txt
|
@ -80,32 +80,6 @@ used. Logarithmic ticks are places at powers of ten and at half those
|
|||
values if there are not to many ticks already (e.g. [1, 5, 10, 50, 100]).
|
||||
For details, see https://github.com/flot/flot/pull/1328
|
||||
|
||||
---
|
||||
This product bundles angular-ui-bootstrap@0.12.1 which is available under a
|
||||
"MIT" license.
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2012-2014 the AngularUI Team, https://github.com/organizations/angular-ui/teams/291112
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---
|
||||
This product bundles bootstrap@3.3.6 which is available under a
|
||||
"MIT" license.
|
||||
|
|
|
@ -107,7 +107,6 @@ export const IGNORE_DIRECTORY_GLOBS = [
|
|||
export const TEMPORARILY_IGNORED_PATHS = [
|
||||
'src/legacy/core_plugins/console/public/src/directives/helpExample.txt',
|
||||
'src/legacy/core_plugins/console/public/src/sense_editor/theme-sense-dark.js',
|
||||
'src/legacy/core_plugins/console/public/webpackShims/ui-bootstrap-custom.js',
|
||||
'src/legacy/core_plugins/kibana/public/assets/play-circle.svg',
|
||||
'src/legacy/core_plugins/tests_bundle/webpackShims/angular-mocks.js',
|
||||
'src/legacy/core_plugins/tile_map/public/__tests__/scaledCircleMarkers.png',
|
||||
|
@ -123,8 +122,6 @@ export const TEMPORARILY_IGNORED_PATHS = [
|
|||
'src/legacy/ui/public/angular-bootstrap/bindHtml/bindHtml.js',
|
||||
'src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-html-unsafe-popup.html',
|
||||
'src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-popup.html',
|
||||
'src/legacy/ui/public/angular-bootstrap/typeahead/typeahead-match.html',
|
||||
'src/legacy/ui/public/angular-bootstrap/typeahead/typeahead-popup.html',
|
||||
'src/legacy/ui/public/assets/favicons/android-chrome-192x192.png',
|
||||
'src/legacy/ui/public/assets/favicons/android-chrome-256x256.png',
|
||||
'src/legacy/ui/public/assets/favicons/android-chrome-512x512.png',
|
||||
|
|
|
@ -21,9 +21,7 @@ import uiRoutes from 'ui/routes';
|
|||
import template from './index.html';
|
||||
|
||||
require('brace');
|
||||
require('ui-bootstrap-custom');
|
||||
|
||||
require('ui/modules').get('kibana', ['sense.ui.bootstrap']);
|
||||
require('ui/tooltip');
|
||||
require('ui/autoload/styles');
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
---
|
||||
root: true
|
|
@ -1,729 +0,0 @@
|
|||
/*
|
||||
* angular-ui-bootstrap
|
||||
* http://angular-ui.github.io/bootstrap/
|
||||
|
||||
* Version: 0.14.2 - 2015-10-17
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
/* @notice
|
||||
* This product bundles angular-ui-bootstrap@0.12.1 which is available under a
|
||||
* "MIT" license.
|
||||
*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012-2014 the AngularUI Team, https://github.com/organizations/angular-ui/teams/291112
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
angular.module("sense.ui.bootstrap", ["sense.ui.bootstrap.tpls","sense.ui.bootstrap.typeahead","sense.ui.bootstrap.position"]);
|
||||
angular.module("sense.ui.bootstrap.tpls", ["sense/template/typeahead/typeahead-match.html","sense/template/typeahead/typeahead-popup.html"]);
|
||||
angular.module('sense.ui.bootstrap.typeahead', ['sense.ui.bootstrap.position'])
|
||||
|
||||
/**
|
||||
* A helper service that can parse typeahead's syntax (string provided by users)
|
||||
* Extracted to a separate service for ease of unit testing
|
||||
*/
|
||||
.factory('senseUibTypeaheadParser', ['$parse', function($parse) {
|
||||
// 00000111000000000000022200000000000000003333333333333330000000000044000
|
||||
var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
|
||||
return {
|
||||
parse: function(input) {
|
||||
var match = input.match(TYPEAHEAD_REGEXP);
|
||||
if (!match) {
|
||||
throw new Error(
|
||||
'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
|
||||
' but got "' + input + '".');
|
||||
}
|
||||
|
||||
return {
|
||||
itemName: match[3],
|
||||
source: $parse(match[4]),
|
||||
viewMapper: $parse(match[2] || match[1]),
|
||||
modelMapper: $parse(match[1])
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
.controller('SenseUibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$senseUibPosition', 'senseUibTypeaheadParser',
|
||||
function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser) {
|
||||
var HOT_KEYS = [9, 13, 27, 38, 40];
|
||||
var eventDebounceTime = 200;
|
||||
var modelCtrl, ngModelOptions;
|
||||
//SUPPORTED ATTRIBUTES (OPTIONS)
|
||||
|
||||
//minimal no of characters that needs to be entered before typeahead kicks-in
|
||||
var minLength = originalScope.$eval(attrs.typeaheadMinLength);
|
||||
if (!minLength && minLength !== 0) {
|
||||
minLength = 1;
|
||||
}
|
||||
|
||||
//minimal wait time after last character typed before typeahead kicks-in
|
||||
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
|
||||
|
||||
//should it restrict model values to the ones selected from the popup only?
|
||||
var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
|
||||
|
||||
//binding to a variable that indicates if matches are being retrieved asynchronously
|
||||
var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
|
||||
|
||||
//a callback executed when a match is selected
|
||||
var onSelectCallback = $parse(attrs.typeaheadOnSelect);
|
||||
|
||||
//should it select highlighted popup value when losing focus?
|
||||
var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
|
||||
|
||||
//binding to a variable that indicates if there were no results after the query is completed
|
||||
var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
|
||||
|
||||
var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
|
||||
|
||||
var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
|
||||
|
||||
var appendToElementId = attrs.typeaheadAppendToElementId || false;
|
||||
|
||||
var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
|
||||
|
||||
//If input matches an item of the list exactly, select it automatically
|
||||
var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
|
||||
|
||||
//INTERNAL VARIABLES
|
||||
|
||||
//model setter executed upon match selection
|
||||
var parsedModel = $parse(attrs.ngModel);
|
||||
var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
|
||||
var $setModelValue = function(scope, newValue) {
|
||||
if (angular.isFunction(parsedModel(originalScope)) &&
|
||||
ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
|
||||
return invokeModelSetter(scope, {$$$p: newValue});
|
||||
} else {
|
||||
return parsedModel.assign(scope, newValue);
|
||||
}
|
||||
};
|
||||
|
||||
//expressions used by typeahead
|
||||
var parserResult = typeaheadParser.parse(attrs.senseUibTypeahead);
|
||||
|
||||
var hasFocus;
|
||||
|
||||
//Used to avoid bug in iOS webview where iOS keyboard does not fire
|
||||
//mousedown & mouseup events
|
||||
//Issue #3699
|
||||
var selected;
|
||||
|
||||
//create a child scope for the typeahead directive so we are not polluting original scope
|
||||
//with typeahead-specific data (matches, query etc.)
|
||||
var scope = originalScope.$new();
|
||||
var offDestroy = originalScope.$on('$destroy', function() {
|
||||
scope.$destroy();
|
||||
});
|
||||
scope.$on('$destroy', offDestroy);
|
||||
|
||||
// WAI-ARIA
|
||||
var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
|
||||
element.attr({
|
||||
'aria-autocomplete': 'list',
|
||||
'aria-expanded': false,
|
||||
'aria-owns': popupId
|
||||
});
|
||||
|
||||
//pop-up element used to display matches
|
||||
var popUpEl = angular.element('<div sense-uib-typeahead-popup></div>');
|
||||
popUpEl.attr({
|
||||
id: popupId,
|
||||
matches: 'matches',
|
||||
active: 'activeIdx',
|
||||
select: 'select(activeIdx)',
|
||||
'move-in-progress': 'moveInProgress',
|
||||
query: 'query',
|
||||
position: 'position'
|
||||
});
|
||||
//custom item template
|
||||
if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
|
||||
popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
|
||||
}
|
||||
|
||||
if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
|
||||
popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
|
||||
}
|
||||
|
||||
var resetMatches = function() {
|
||||
scope.matches = [];
|
||||
scope.activeIdx = -1;
|
||||
element.attr('aria-expanded', false);
|
||||
};
|
||||
|
||||
var getMatchId = function(index) {
|
||||
return popupId + '-option-' + index;
|
||||
};
|
||||
|
||||
// Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
|
||||
// This attribute is added or removed automatically when the `activeIdx` changes.
|
||||
scope.$watch('activeIdx', function(index) {
|
||||
if (index < 0) {
|
||||
element.removeAttr('aria-activedescendant');
|
||||
} else {
|
||||
element.attr('aria-activedescendant', getMatchId(index));
|
||||
}
|
||||
});
|
||||
|
||||
var inputIsExactMatch = function(inputValue, index) {
|
||||
if (scope.matches.length > index && inputValue) {
|
||||
return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
var getMatchesAsync = function(inputValue) {
|
||||
var locals = {$viewValue: inputValue};
|
||||
isLoadingSetter(originalScope, true);
|
||||
isNoResultsSetter(originalScope, false);
|
||||
$q.when(parserResult.source(originalScope, locals)).then(function(matches) {
|
||||
//it might happen that several async queries were in progress if a user were typing fast
|
||||
//but we are interested only in responses that correspond to the current view value
|
||||
var onCurrentRequest = (inputValue === modelCtrl.$viewValue);
|
||||
if (onCurrentRequest && hasFocus) {
|
||||
if (matches && matches.length > 0) {
|
||||
scope.activeIdx = focusFirst ? 0 : -1;
|
||||
isNoResultsSetter(originalScope, false);
|
||||
scope.matches.length = 0;
|
||||
|
||||
//transform labels
|
||||
for (var i = 0; i < matches.length; i++) {
|
||||
locals[parserResult.itemName] = matches[i];
|
||||
scope.matches.push({
|
||||
id: getMatchId(i),
|
||||
label: parserResult.viewMapper(scope, locals),
|
||||
model: matches[i]
|
||||
});
|
||||
}
|
||||
|
||||
scope.query = inputValue;
|
||||
//position pop-up with matches - we need to re-calculate its position each time we are opening a window
|
||||
//with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
|
||||
//due to other elements being rendered
|
||||
recalculatePosition();
|
||||
|
||||
element.attr('aria-expanded', true);
|
||||
|
||||
//Select the single remaining option if user input matches
|
||||
if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
|
||||
scope.select(0);
|
||||
}
|
||||
} else {
|
||||
resetMatches();
|
||||
isNoResultsSetter(originalScope, true);
|
||||
}
|
||||
}
|
||||
if (onCurrentRequest) {
|
||||
isLoadingSetter(originalScope, false);
|
||||
}
|
||||
}, function() {
|
||||
resetMatches();
|
||||
isLoadingSetter(originalScope, false);
|
||||
isNoResultsSetter(originalScope, true);
|
||||
});
|
||||
};
|
||||
|
||||
// bind events only if appendToBody params exist - performance feature
|
||||
if (appendToBody) {
|
||||
angular.element($window).bind('resize', fireRecalculating);
|
||||
$document.find('body').bind('scroll', fireRecalculating);
|
||||
}
|
||||
|
||||
// Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
|
||||
var timeoutEventPromise;
|
||||
|
||||
// Default progress type
|
||||
scope.moveInProgress = false;
|
||||
|
||||
function fireRecalculating() {
|
||||
if (!scope.moveInProgress) {
|
||||
scope.moveInProgress = true;
|
||||
scope.$digest();
|
||||
}
|
||||
|
||||
// Cancel previous timeout
|
||||
if (timeoutEventPromise) {
|
||||
$timeout.cancel(timeoutEventPromise);
|
||||
}
|
||||
|
||||
// Debounced executing recalculate after events fired
|
||||
timeoutEventPromise = $timeout(function() {
|
||||
// if popup is visible
|
||||
if (scope.matches.length) {
|
||||
recalculatePosition();
|
||||
}
|
||||
|
||||
scope.moveInProgress = false;
|
||||
}, eventDebounceTime);
|
||||
}
|
||||
|
||||
// recalculate actual position and set new values to scope
|
||||
// after digest loop is popup in right position
|
||||
function recalculatePosition() {
|
||||
scope.position = appendToBody ? $position.offset(element) : $position.position(element);
|
||||
scope.position.top += element.prop('offsetHeight');
|
||||
}
|
||||
|
||||
//we need to propagate user's query so we can highlight matches
|
||||
scope.query = undefined;
|
||||
|
||||
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
|
||||
var timeoutPromise;
|
||||
|
||||
var scheduleSearchWithTimeout = function(inputValue) {
|
||||
timeoutPromise = $timeout(function() {
|
||||
getMatchesAsync(inputValue);
|
||||
}, waitTime);
|
||||
};
|
||||
|
||||
var cancelPreviousTimeout = function() {
|
||||
if (timeoutPromise) {
|
||||
$timeout.cancel(timeoutPromise);
|
||||
}
|
||||
};
|
||||
|
||||
resetMatches();
|
||||
|
||||
scope.select = function(activeIdx) {
|
||||
//called from within the $digest() cycle
|
||||
var locals = {};
|
||||
var model, item;
|
||||
|
||||
selected = true;
|
||||
locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
|
||||
model = parserResult.modelMapper(originalScope, locals);
|
||||
$setModelValue(originalScope, model);
|
||||
modelCtrl.$setValidity('editable', true);
|
||||
modelCtrl.$setValidity('parse', true);
|
||||
|
||||
onSelectCallback(originalScope, {
|
||||
$item: item,
|
||||
$model: model,
|
||||
$label: parserResult.viewMapper(originalScope, locals)
|
||||
});
|
||||
|
||||
resetMatches();
|
||||
|
||||
//return focus to the input element if a match was selected via a mouse click event
|
||||
// use timeout to avoid $rootScope:inprog error
|
||||
if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
|
||||
$timeout(function() { element[0].focus(); }, 0, false);
|
||||
}
|
||||
};
|
||||
|
||||
//bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
|
||||
element.bind('keydown', function(evt) {
|
||||
//typeahead is open and an "interesting" key was pressed
|
||||
if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
|
||||
if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
|
||||
resetMatches();
|
||||
scope.$digest();
|
||||
return;
|
||||
}
|
||||
|
||||
evt.preventDefault();
|
||||
|
||||
if (evt.which === 40) {
|
||||
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
|
||||
scope.$digest();
|
||||
} else if (evt.which === 38) {
|
||||
scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
|
||||
scope.$digest();
|
||||
} else if (evt.which === 13 || evt.which === 9) {
|
||||
scope.$apply(function () {
|
||||
scope.select(scope.activeIdx);
|
||||
});
|
||||
} else if (evt.which === 27) {
|
||||
evt.stopPropagation();
|
||||
|
||||
resetMatches();
|
||||
scope.$digest();
|
||||
}
|
||||
});
|
||||
|
||||
element.bind('blur', function() {
|
||||
if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
|
||||
selected = true;
|
||||
scope.$apply(function() {
|
||||
scope.select(scope.activeIdx);
|
||||
});
|
||||
}
|
||||
hasFocus = false;
|
||||
selected = false;
|
||||
});
|
||||
|
||||
// Keep reference to click handler to unbind it.
|
||||
var dismissClickHandler = function(evt) {
|
||||
// Issue #3973
|
||||
// Firefox treats right click as a click on document
|
||||
if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
|
||||
resetMatches();
|
||||
if (!$rootScope.$$phase) {
|
||||
scope.$digest();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$document.bind('click', dismissClickHandler);
|
||||
|
||||
originalScope.$on('$destroy', function() {
|
||||
$document.unbind('click', dismissClickHandler);
|
||||
if (appendToBody || appendToElementId) {
|
||||
$popup.remove();
|
||||
}
|
||||
// Prevent jQuery cache memory leak
|
||||
popUpEl.remove();
|
||||
});
|
||||
|
||||
var $popup = $compile(popUpEl)(scope);
|
||||
|
||||
if (appendToBody) {
|
||||
$document.find('body').append($popup);
|
||||
} else if (appendToElementId !== false) {
|
||||
angular.element($document[0].getElementById(appendToElementId)).append($popup);
|
||||
} else {
|
||||
element.after($popup);
|
||||
}
|
||||
|
||||
this.init = function(_modelCtrl, _ngModelOptions) {
|
||||
modelCtrl = _modelCtrl;
|
||||
ngModelOptions = _ngModelOptions;
|
||||
|
||||
//plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
|
||||
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
|
||||
modelCtrl.$parsers.unshift(function(inputValue) {
|
||||
hasFocus = true;
|
||||
|
||||
if (minLength === 0 || inputValue && inputValue.length >= minLength) {
|
||||
if (waitTime > 0) {
|
||||
cancelPreviousTimeout();
|
||||
scheduleSearchWithTimeout(inputValue);
|
||||
} else {
|
||||
getMatchesAsync(inputValue);
|
||||
}
|
||||
} else {
|
||||
isLoadingSetter(originalScope, false);
|
||||
cancelPreviousTimeout();
|
||||
resetMatches();
|
||||
}
|
||||
|
||||
if (isEditable) {
|
||||
return inputValue;
|
||||
} else {
|
||||
if (!inputValue) {
|
||||
// Reset in case user had typed something previously.
|
||||
modelCtrl.$setValidity('editable', true);
|
||||
return null;
|
||||
} else {
|
||||
modelCtrl.$setValidity('editable', false);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modelCtrl.$formatters.push(function(modelValue) {
|
||||
var candidateViewValue, emptyViewValue;
|
||||
var locals = {};
|
||||
|
||||
// The validity may be set to false via $parsers (see above) if
|
||||
// the model is restricted to selected values. If the model
|
||||
// is set manually it is considered to be valid.
|
||||
if (!isEditable) {
|
||||
modelCtrl.$setValidity('editable', true);
|
||||
}
|
||||
|
||||
if (inputFormatter) {
|
||||
locals.$model = modelValue;
|
||||
return inputFormatter(originalScope, locals);
|
||||
} else {
|
||||
//it might happen that we don't have enough info to properly render input value
|
||||
//we need to check for this situation and simply return model value if we can't apply custom formatting
|
||||
locals[parserResult.itemName] = modelValue;
|
||||
candidateViewValue = parserResult.viewMapper(originalScope, locals);
|
||||
locals[parserResult.itemName] = undefined;
|
||||
emptyViewValue = parserResult.viewMapper(originalScope, locals);
|
||||
|
||||
return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
|
||||
}
|
||||
});
|
||||
};
|
||||
}])
|
||||
|
||||
.directive('senseUibTypeahead', function() {
|
||||
return {
|
||||
controller: 'SenseUibTypeaheadController',
|
||||
require: ['ngModel', '^?ngModelOptions', 'senseUibTypeahead'],
|
||||
link: function(originalScope, element, attrs, ctrls) {
|
||||
ctrls[2].init(ctrls[0], ctrls[1]);
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
.directive('senseUibTypeaheadPopup', function() {
|
||||
return {
|
||||
scope: {
|
||||
matches: '=',
|
||||
query: '=',
|
||||
active: '=',
|
||||
position: '&',
|
||||
moveInProgress: '=',
|
||||
select: '&'
|
||||
},
|
||||
replace: true,
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.popupTemplateUrl || 'sense/template/typeahead/typeahead-popup.html';
|
||||
},
|
||||
link: function(scope, element, attrs) {
|
||||
scope.templateUrl = attrs.templateUrl;
|
||||
|
||||
scope.isOpen = function() {
|
||||
return scope.matches.length > 0;
|
||||
};
|
||||
|
||||
scope.isActive = function(matchIdx) {
|
||||
return scope.active == matchIdx;
|
||||
};
|
||||
|
||||
scope.selectActive = function(matchIdx) {
|
||||
scope.active = matchIdx;
|
||||
};
|
||||
|
||||
scope.selectMatch = function(activeIdx) {
|
||||
scope.select({activeIdx:activeIdx});
|
||||
};
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
.directive('senseUibTypeaheadMatch', ['$templateCache', '$compile', '$parse', function($templateCache, $compile, $parse) {
|
||||
return {
|
||||
scope: {
|
||||
index: '=',
|
||||
match: '=',
|
||||
query: '='
|
||||
},
|
||||
link:function(scope, element, attrs) {
|
||||
var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'sense/template/typeahead/typeahead-match.html';
|
||||
$compile($templateCache.get(tplUrl).trim())(scope, function(clonedElement) {
|
||||
element.replaceWith(clonedElement);
|
||||
});
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
.filter('senseUibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
|
||||
var isSanitizePresent;
|
||||
isSanitizePresent = $injector.has('$sanitize');
|
||||
|
||||
function escapeRegexp(queryToEscape) {
|
||||
// Regex: capture the whole query string and replace it with the string that will be used to match
|
||||
// the results, for example if the capture is "a" the result will be \a
|
||||
return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
|
||||
}
|
||||
|
||||
function containsHtml(matchItem) {
|
||||
return /<.*>/g.test(matchItem);
|
||||
}
|
||||
|
||||
return function(matchItem, query) {
|
||||
if (!isSanitizePresent && containsHtml(matchItem)) {
|
||||
$log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
|
||||
}
|
||||
matchItem = query? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag
|
||||
if (!isSanitizePresent) {
|
||||
matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
|
||||
}
|
||||
return matchItem;
|
||||
};
|
||||
}]);
|
||||
|
||||
angular.module('sense.ui.bootstrap.position', [])
|
||||
|
||||
/**
|
||||
* A set of utility methods that can be use to retrieve position of DOM elements.
|
||||
* It is meant to be used where we need to absolute-position DOM elements in
|
||||
* relation to other, existing elements (this is the case for tooltips, popovers,
|
||||
* typeahead suggestions etc.).
|
||||
*/
|
||||
.factory('$senseUibPosition', ['$document', '$window', function($document, $window) {
|
||||
function getStyle(el, cssprop) {
|
||||
if (el.currentStyle) { //IE
|
||||
return el.currentStyle[cssprop];
|
||||
} else if ($window.getComputedStyle) {
|
||||
return $window.getComputedStyle(el)[cssprop];
|
||||
}
|
||||
// finally try and get inline style
|
||||
return el.style[cssprop];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given element is statically positioned
|
||||
* @param element - raw DOM element
|
||||
*/
|
||||
function isStaticPositioned(element) {
|
||||
return (getStyle(element, 'position') || 'static' ) === 'static';
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the closest, non-statically positioned parentOffset of a given element
|
||||
* @param element
|
||||
*/
|
||||
var parentOffsetEl = function(element) {
|
||||
var docDomEl = $document[0];
|
||||
var offsetParent = element.offsetParent || docDomEl;
|
||||
while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
|
||||
offsetParent = offsetParent.offsetParent;
|
||||
}
|
||||
return offsetParent || docDomEl;
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Provides read-only equivalent of jQuery's position function:
|
||||
* http://api.jquery.com/position/
|
||||
*/
|
||||
position: function(element) {
|
||||
var elBCR = this.offset(element);
|
||||
var offsetParentBCR = { top: 0, left: 0 };
|
||||
var offsetParentEl = parentOffsetEl(element[0]);
|
||||
if (offsetParentEl != $document[0]) {
|
||||
offsetParentBCR = this.offset(angular.element(offsetParentEl));
|
||||
offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
|
||||
offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
|
||||
}
|
||||
|
||||
var boundingClientRect = element[0].getBoundingClientRect();
|
||||
return {
|
||||
width: boundingClientRect.width || element.prop('offsetWidth'),
|
||||
height: boundingClientRect.height || element.prop('offsetHeight'),
|
||||
top: elBCR.top - offsetParentBCR.top,
|
||||
left: elBCR.left - offsetParentBCR.left
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Provides read-only equivalent of jQuery's offset function:
|
||||
* http://api.jquery.com/offset/
|
||||
*/
|
||||
offset: function(element) {
|
||||
var boundingClientRect = element[0].getBoundingClientRect();
|
||||
return {
|
||||
width: boundingClientRect.width || element.prop('offsetWidth'),
|
||||
height: boundingClientRect.height || element.prop('offsetHeight'),
|
||||
top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
|
||||
left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Provides coordinates for the targetEl in relation to hostEl
|
||||
*/
|
||||
positionElements: function(hostEl, targetEl, positionStr, appendToBody) {
|
||||
var positionStrParts = positionStr.split('-');
|
||||
var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
|
||||
|
||||
var hostElPos,
|
||||
targetElWidth,
|
||||
targetElHeight,
|
||||
targetElPos;
|
||||
|
||||
hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
|
||||
|
||||
targetElWidth = targetEl.prop('offsetWidth');
|
||||
targetElHeight = targetEl.prop('offsetHeight');
|
||||
|
||||
var shiftWidth = {
|
||||
center: function() {
|
||||
return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
|
||||
},
|
||||
left: function() {
|
||||
return hostElPos.left;
|
||||
},
|
||||
right: function() {
|
||||
return hostElPos.left + hostElPos.width;
|
||||
}
|
||||
};
|
||||
|
||||
var shiftHeight = {
|
||||
center: function() {
|
||||
return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
|
||||
},
|
||||
top: function() {
|
||||
return hostElPos.top;
|
||||
},
|
||||
bottom: function() {
|
||||
return hostElPos.top + hostElPos.height;
|
||||
}
|
||||
};
|
||||
|
||||
switch (pos0) {
|
||||
case 'right':
|
||||
targetElPos = {
|
||||
top: shiftHeight[pos1](),
|
||||
left: shiftWidth[pos0]()
|
||||
};
|
||||
break;
|
||||
case 'left':
|
||||
targetElPos = {
|
||||
top: shiftHeight[pos1](),
|
||||
left: hostElPos.left - targetElWidth
|
||||
};
|
||||
break;
|
||||
case 'bottom':
|
||||
targetElPos = {
|
||||
top: shiftHeight[pos0](),
|
||||
left: shiftWidth[pos1]()
|
||||
};
|
||||
break;
|
||||
default:
|
||||
targetElPos = {
|
||||
top: hostElPos.top - targetElHeight,
|
||||
left: shiftWidth[pos1]()
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return targetElPos;
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
angular.module("sense/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
|
||||
$templateCache.put("sense/template/typeahead/typeahead-match.html",
|
||||
"<a href tabindex=\"-1\" ng-bind-html=\"match.label | senseUibTypeaheadHighlight:query\"></a>\n" +
|
||||
"");
|
||||
}]);
|
||||
|
||||
angular.module("sense/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
|
||||
$templateCache.put("sense/template/typeahead/typeahead-popup.html",
|
||||
"<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
|
||||
" <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{match.id}}\">\n" +
|
||||
" <div sense-uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
|
||||
" </li>\n" +
|
||||
"</ul>\n" +
|
||||
"");
|
||||
}]);
|
|
@ -1,10 +0,0 @@
|
|||
<div class="alert" ng-class="['alert-' + (type || 'warning'), closeable ? 'alert-dismissable' : null]" role="alert">
|
||||
<button ng-show="closeable" type="button" class="close" ng-click="close()">
|
||||
<span aria-hidden="true">×</span>
|
||||
<span class="euiScreenReaderOnly"
|
||||
i18n-id="common.ui.angularBootstrap.alert.closeButtonLabel"
|
||||
i18n-default-message="Close"
|
||||
></span>
|
||||
</button>
|
||||
<div ng-transclude></div>
|
||||
</div>
|
|
@ -1,20 +0,0 @@
|
|||
angular.module('ui.bootstrap.alert', [])
|
||||
|
||||
.controller('AlertController', ['$scope', '$attrs', function ($scope, $attrs) {
|
||||
$scope.closeable = 'close' in $attrs;
|
||||
this.close = $scope.close;
|
||||
}])
|
||||
|
||||
.directive('alert', function () {
|
||||
return {
|
||||
restrict:'EA',
|
||||
controller:'AlertController',
|
||||
templateUrl:'template/alert/alert.html',
|
||||
transclude:true,
|
||||
replace:true,
|
||||
scope: {
|
||||
type: '@',
|
||||
close: '&'
|
||||
}
|
||||
};
|
||||
})
|
86
src/legacy/ui/public/angular-bootstrap/index.js
vendored
86
src/legacy/ui/public/angular-bootstrap/index.js
vendored
|
@ -23,49 +23,27 @@ uiModules.get('kibana', [
|
|||
angular.module('ui.bootstrap', [
|
||||
'ui.bootstrap.tpls',
|
||||
'ui.bootstrap.transition',
|
||||
'ui.bootstrap.alert',
|
||||
'ui.bootstrap.bindHtml',
|
||||
'ui.bootstrap.position',
|
||||
'ui.bootstrap.modal',
|
||||
'ui.bootstrap.pagination',
|
||||
'ui.bootstrap.tooltip',
|
||||
'ui.bootstrap.progressbar',
|
||||
'ui.bootstrap.timepicker',
|
||||
'ui.bootstrap.typeahead'
|
||||
]);
|
||||
|
||||
angular.module('ui.bootstrap.tpls', [
|
||||
'template/alert/alert.html',
|
||||
'template/modal/backdrop.html',
|
||||
'template/modal/window.html',
|
||||
'template/pagination/pager.html',
|
||||
'template/pagination/pagination.html',
|
||||
'template/tooltip/tooltip-html-unsafe-popup.html',
|
||||
'template/tooltip/tooltip-popup.html',
|
||||
'template/progressbar/bar.html',
|
||||
'template/modal/window.html',
|
||||
'template/tooltip/tooltip-html-unsafe-popup.html',
|
||||
'template/tooltip/tooltip-popup.html',
|
||||
'template/progressbar/bar.html',
|
||||
'template/progressbar/progress.html',
|
||||
'template/progressbar/progressbar.html',
|
||||
'template/timepicker/timepicker.html',
|
||||
'template/typeahead/typeahead-match.html',
|
||||
'template/typeahead/typeahead-popup.html'
|
||||
]);
|
||||
|
||||
import './alert/alert';
|
||||
import './bindHtml/bindHtml';
|
||||
import './modal/modal';
|
||||
import './pagination/pagination';
|
||||
import './position/position';
|
||||
import './progressbar/progressbar';
|
||||
import './timepicker/timepicker';
|
||||
import './tooltip/tooltip';
|
||||
import './transition/transition';
|
||||
import './typeahead/typeahead';
|
||||
|
||||
import alert from './alert/alert.html';
|
||||
|
||||
angular.module('template/alert/alert.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/alert/alert.html', alert);
|
||||
}]);
|
||||
|
||||
import backdrop from './modal/backdrop.html';
|
||||
|
||||
|
@ -79,35 +57,23 @@ angular.module('template/modal/window.html', []).run(['$templateCache', function
|
|||
$templateCache.put('template/modal/window.html', modal);
|
||||
}]);
|
||||
|
||||
import pager from './pagination/pager.html';
|
||||
import tooltipUnsafePopup from './tooltip/tooltip-html-unsafe-popup.html';
|
||||
|
||||
angular.module('template/pagination/pager.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/pagination/pager.html', pager);
|
||||
angular.module('template/tooltip/tooltip-html-unsafe-popup.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/tooltip/tooltip-html-unsafe-popup.html', tooltipUnsafePopup);
|
||||
}]);
|
||||
|
||||
import tooltipPopup from './tooltip/tooltip-popup.html';
|
||||
|
||||
angular.module('template/tooltip/tooltip-popup.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/tooltip/tooltip-popup.html', tooltipPopup);
|
||||
}]);
|
||||
|
||||
import pagination from './pagination/pagination.html';
|
||||
import bar from './progressbar/bar.html';
|
||||
|
||||
angular.module('template/pagination/pagination.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/pagination/pagination.html', pagination);
|
||||
}]);
|
||||
|
||||
import tooltipUnsafePopup from './tooltip/tooltip-html-unsafe-popup.html';
|
||||
|
||||
angular.module('template/tooltip/tooltip-html-unsafe-popup.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/tooltip/tooltip-html-unsafe-popup.html', tooltipUnsafePopup);
|
||||
}]);
|
||||
|
||||
import tooltipPopup from './tooltip/tooltip-popup.html';
|
||||
|
||||
angular.module('template/tooltip/tooltip-popup.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/tooltip/tooltip-popup.html', tooltipPopup);
|
||||
}]);
|
||||
|
||||
import bar from './progressbar/bar.html';
|
||||
|
||||
angular.module('template/progressbar/bar.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/progressbar/bar.html', bar);
|
||||
}]);
|
||||
angular.module('template/progressbar/bar.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/progressbar/bar.html', bar);
|
||||
}]);
|
||||
|
||||
import progress from './progressbar/progress.html';
|
||||
|
||||
|
@ -120,21 +86,3 @@ import progressbar from './progressbar/progressbar.html';
|
|||
angular.module('template/progressbar/progressbar.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/progressbar/progressbar.html', progressbar);
|
||||
}]);
|
||||
|
||||
import timepicker from './timepicker/timepicker.html';
|
||||
|
||||
angular.module('template/timepicker/timepicker.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/timepicker/timepicker.html', timepicker);
|
||||
}]);
|
||||
|
||||
import typeaheadMatch from './typeahead/typeahead-match.html';
|
||||
|
||||
angular.module('template/typeahead/typeahead-match.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/typeahead/typeahead-match.html', typeaheadMatch);
|
||||
}]);
|
||||
|
||||
import typeaheadPopup from './typeahead/typeahead-popup.html';
|
||||
|
||||
angular.module('template/typeahead/typeahead-popup.html', []).run(['$templateCache', function($templateCache) {
|
||||
$templateCache.put('template/typeahead/typeahead-popup.html', typeaheadPopup);
|
||||
}]);
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
<ul class="pager">
|
||||
<li ng-class="{disabled: noPrevious(), previous: align}"><a href ng-click="selectPage(page - 1)">{{getText('previous')}}</a></li>
|
||||
<li ng-class="{disabled: noNext(), next: align}"><a href ng-click="selectPage(page + 1)">{{getText('next')}}</a></li>
|
||||
</ul>
|
|
@ -1,7 +0,0 @@
|
|||
<ul class="pagination">
|
||||
<li ng-if="boundaryLinks" ng-class="{disabled: noPrevious()}"><a href ng-click="selectPage(1)">{{getText('first')}}</a></li>
|
||||
<li ng-if="directionLinks" ng-class="{disabled: noPrevious()}"><a href ng-click="selectPage(page - 1)">{{getText('previous')}}</a></li>
|
||||
<li ng-repeat="page in pages track by $index" ng-class="{active: page.active}"><a href ng-click="selectPage(page.number)">{{page.text}}</a></li>
|
||||
<li ng-if="directionLinks" ng-class="{disabled: noNext()}"><a href ng-click="selectPage(page + 1)">{{getText('next')}}</a></li>
|
||||
<li ng-if="boundaryLinks" ng-class="{disabled: noNext()}"><a href ng-click="selectPage(totalPages)">{{getText('last')}}</a></li>
|
||||
</ul>
|
|
@ -1,228 +0,0 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
angular.module('ui.bootstrap.pagination', [])
|
||||
|
||||
.controller('PaginationController', ['$scope', '$attrs', '$parse', function ($scope, $attrs, $parse) {
|
||||
var self = this,
|
||||
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
|
||||
setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
|
||||
|
||||
this.init = function(ngModelCtrl_, config) {
|
||||
ngModelCtrl = ngModelCtrl_;
|
||||
this.config = config;
|
||||
|
||||
ngModelCtrl.$render = function() {
|
||||
self.render();
|
||||
};
|
||||
|
||||
if ($attrs.itemsPerPage) {
|
||||
$scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
|
||||
self.itemsPerPage = parseInt(value, 10);
|
||||
$scope.totalPages = self.calculateTotalPages();
|
||||
});
|
||||
} else {
|
||||
this.itemsPerPage = config.itemsPerPage;
|
||||
}
|
||||
};
|
||||
|
||||
this.calculateTotalPages = function() {
|
||||
var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
|
||||
return Math.max(totalPages || 0, 1);
|
||||
};
|
||||
|
||||
this.render = function() {
|
||||
$scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1;
|
||||
};
|
||||
|
||||
$scope.selectPage = function(page) {
|
||||
if ( $scope.page !== page && page > 0 && page <= $scope.totalPages) {
|
||||
ngModelCtrl.$setViewValue(page);
|
||||
ngModelCtrl.$render();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getText = function( key ) {
|
||||
return $scope[key + 'Text'] || self.config[key + 'Text'];
|
||||
};
|
||||
$scope.noPrevious = function() {
|
||||
return $scope.page === 1;
|
||||
};
|
||||
$scope.noNext = function() {
|
||||
return $scope.page === $scope.totalPages;
|
||||
};
|
||||
|
||||
$scope.$watch('totalItems', function() {
|
||||
$scope.totalPages = self.calculateTotalPages();
|
||||
});
|
||||
|
||||
$scope.$watch('totalPages', function(value) {
|
||||
setNumPages($scope.$parent, value); // Readonly variable
|
||||
|
||||
if ( $scope.page > value ) {
|
||||
$scope.selectPage(value);
|
||||
} else {
|
||||
ngModelCtrl.$render();
|
||||
}
|
||||
});
|
||||
}])
|
||||
|
||||
.constant('paginationConfig', {
|
||||
itemsPerPage: 10,
|
||||
boundaryLinks: false,
|
||||
directionLinks: true,
|
||||
firstText: i18n.translate('common.ui.angularBootstrap.pagination.paginationConfig.firstLabel', {
|
||||
defaultMessage: 'First'
|
||||
}),
|
||||
previousText: i18n.translate('common.ui.angularBootstrap.pagination.paginationConfig.previousLabel', {
|
||||
defaultMessage: 'Previous'
|
||||
}),
|
||||
nextText: i18n.translate('common.ui.angularBootstrap.pagination.paginationConfig.nextLabel', {
|
||||
defaultMessage: 'Next'
|
||||
}),
|
||||
lastText: i18n.translate('common.ui.angularBootstrap.pagination.paginationConfig.lastLabel', {
|
||||
defaultMessage: 'Last'
|
||||
}),
|
||||
rotate: true
|
||||
})
|
||||
|
||||
.directive('pagination', ['$parse', 'paginationConfig', function($parse, paginationConfig) {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
scope: {
|
||||
totalItems: '=',
|
||||
firstText: '@',
|
||||
previousText: '@',
|
||||
nextText: '@',
|
||||
lastText: '@'
|
||||
},
|
||||
require: ['pagination', '?ngModel'],
|
||||
controller: 'PaginationController',
|
||||
templateUrl: 'template/pagination/pagination.html',
|
||||
replace: true,
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
||||
|
||||
if (!ngModelCtrl) {
|
||||
return; // do nothing if no ng-model
|
||||
}
|
||||
|
||||
// Setup configuration parameters
|
||||
var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize,
|
||||
rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate;
|
||||
scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
|
||||
scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks;
|
||||
|
||||
paginationCtrl.init(ngModelCtrl, paginationConfig);
|
||||
|
||||
if (attrs.maxSize) {
|
||||
scope.$parent.$watch($parse(attrs.maxSize), function(value) {
|
||||
maxSize = parseInt(value, 10);
|
||||
paginationCtrl.render();
|
||||
});
|
||||
}
|
||||
|
||||
// Create page object used in template
|
||||
function makePage(number, text, isActive) {
|
||||
return {
|
||||
number: number,
|
||||
text: text,
|
||||
active: isActive
|
||||
};
|
||||
}
|
||||
|
||||
function getPages(currentPage, totalPages) {
|
||||
var pages = [];
|
||||
|
||||
// Default page limits
|
||||
var startPage = 1, endPage = totalPages;
|
||||
var isMaxSized = ( angular.isDefined(maxSize) && maxSize < totalPages );
|
||||
|
||||
// recompute if maxSize
|
||||
if ( isMaxSized ) {
|
||||
if ( rotate ) {
|
||||
// Current page is displayed in the middle of the visible ones
|
||||
startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);
|
||||
endPage = startPage + maxSize - 1;
|
||||
|
||||
// Adjust if limit is exceeded
|
||||
if (endPage > totalPages) {
|
||||
endPage = totalPages;
|
||||
startPage = endPage - maxSize + 1;
|
||||
}
|
||||
} else {
|
||||
// Visible pages are paginated with maxSize
|
||||
startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;
|
||||
|
||||
// Adjust last page if limit is exceeded
|
||||
endPage = Math.min(startPage + maxSize - 1, totalPages);
|
||||
}
|
||||
}
|
||||
|
||||
// Add page number links
|
||||
for (var number = startPage; number <= endPage; number++) {
|
||||
var page = makePage(number, number, number === currentPage);
|
||||
pages.push(page);
|
||||
}
|
||||
|
||||
// Add links to move between page sets
|
||||
if ( isMaxSized && ! rotate ) {
|
||||
if ( startPage > 1 ) {
|
||||
var previousPageSet = makePage(startPage - 1, '...', false);
|
||||
pages.unshift(previousPageSet);
|
||||
}
|
||||
|
||||
if ( endPage < totalPages ) {
|
||||
var nextPageSet = makePage(endPage + 1, '...', false);
|
||||
pages.push(nextPageSet);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
var originalRender = paginationCtrl.render;
|
||||
paginationCtrl.render = function() {
|
||||
originalRender();
|
||||
if (scope.page > 0 && scope.page <= scope.totalPages) {
|
||||
scope.pages = getPages(scope.page, scope.totalPages);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
.constant('pagerConfig', {
|
||||
itemsPerPage: 10,
|
||||
previousText: i18n.translate('common.ui.angularBootstrap.pagination.pagerConfig.previousLabel', {
|
||||
defaultMessage: '« Previous'
|
||||
}),
|
||||
nextText: i18n.translate('common.ui.angularBootstrap.pagination.pagerConfig.nextLabel', {
|
||||
defaultMessage: 'Next »'
|
||||
}),
|
||||
align: true
|
||||
})
|
||||
|
||||
.directive('pager', ['pagerConfig', function(pagerConfig) {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
scope: {
|
||||
totalItems: '=',
|
||||
previousText: '@',
|
||||
nextText: '@'
|
||||
},
|
||||
require: ['pager', '?ngModel'],
|
||||
controller: 'PaginationController',
|
||||
templateUrl: 'template/pagination/pager.html',
|
||||
replace: true,
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
||||
|
||||
if (!ngModelCtrl) {
|
||||
return; // do nothing if no ng-model
|
||||
}
|
||||
|
||||
scope.align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : pagerConfig.align;
|
||||
paginationCtrl.init(ngModelCtrl, pagerConfig);
|
||||
}
|
||||
};
|
||||
}]);
|
2
src/legacy/ui/public/angular-bootstrap/progressbar/bar.html
Executable file → Normal file
2
src/legacy/ui/public/angular-bootstrap/progressbar/bar.html
Executable file → Normal file
|
@ -1 +1 @@
|
|||
<div class="progress-bar" ng-class="type && 'progress-bar-' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: percent + '%'}" aria-valuetext="{{percent | number:0}}%" ng-transclude></div>
|
||||
<div class="progress-bar" ng-class="type && 'progress-bar-' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: percent + '%'}" aria-valuetext="{{percent | number:0}}%" ng-transclude></div>
|
|
@ -34,33 +34,33 @@ angular.module('ui.bootstrap.progressbar', [])
|
|||
};
|
||||
}])
|
||||
|
||||
.directive('progress', function() {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
controller: 'ProgressController',
|
||||
require: 'progress',
|
||||
scope: {},
|
||||
templateUrl: 'template/progressbar/progress.html'
|
||||
};
|
||||
})
|
||||
.directive('progress', function() {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
controller: 'ProgressController',
|
||||
require: 'progress',
|
||||
scope: {},
|
||||
templateUrl: 'template/progressbar/progress.html'
|
||||
};
|
||||
})
|
||||
|
||||
.directive('bar', function() {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
require: '^progress',
|
||||
scope: {
|
||||
value: '=',
|
||||
type: '@'
|
||||
},
|
||||
templateUrl: 'template/progressbar/bar.html',
|
||||
link: function(scope, element, attrs, progressCtrl) {
|
||||
progressCtrl.addBar(scope, element);
|
||||
}
|
||||
};
|
||||
.directive('bar', function() {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
require: '^progress',
|
||||
scope: {
|
||||
value: '=',
|
||||
type: '@'
|
||||
},
|
||||
templateUrl: 'template/progressbar/bar.html',
|
||||
link: function(scope, element, attrs, progressCtrl) {
|
||||
progressCtrl.addBar(scope, element);
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
.directive('progressbar', function() {
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<table>
|
||||
<tbody>
|
||||
<tr class="text-center">
|
||||
<td><a ng-click="incrementHours()" class="kuiButton kuiButton--hollow"><span class="fa fa-chevron-up"></span></a></td>
|
||||
<td> </td>
|
||||
<td><a ng-click="incrementMinutes()" class="kuiButton kuiButton--hollow"><span class="fa fa-chevron-up"></span></a></td>
|
||||
<td ng-show="showMeridian"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:50px;" class="form-group" ng-class="{'has-error': invalidHours}">
|
||||
<input type="text" ng-model="hours" ng-change="updateHours()" class="form-control text-center" ng-mousewheel="incrementHours()" ng-readonly="readonlyInput" maxlength="2">
|
||||
</td>
|
||||
<td>:</td>
|
||||
<td style="width:50px;" class="form-group" ng-class="{'has-error': invalidMinutes}">
|
||||
<input type="text" ng-model="minutes" ng-change="updateMinutes()" class="form-control text-center" ng-readonly="readonlyInput" maxlength="2">
|
||||
</td>
|
||||
<td ng-show="showMeridian"><button type="button" class="kuiButton kuiButton--basic text-center" ng-click="toggleMeridian()">{{meridian}}</button></td>
|
||||
</tr>
|
||||
<tr class="text-center">
|
||||
<td><a ng-click="decrementHours()" class="kuiButton kuiButton--hollow"><span class="fa fa-chevron-down"></span></a></td>
|
||||
<td> </td>
|
||||
<td><a ng-click="decrementMinutes()" class="kuiButton kuiButton--hollow"><span class="fa fa-chevron-down"></span></a></td>
|
||||
<td ng-show="showMeridian"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
|
@ -1,254 +0,0 @@
|
|||
angular.module('ui.bootstrap.timepicker', [])
|
||||
|
||||
.constant('timepickerConfig', {
|
||||
hourStep: 1,
|
||||
minuteStep: 1,
|
||||
showMeridian: true,
|
||||
meridians: null,
|
||||
readonlyInput: false,
|
||||
mousewheel: true
|
||||
})
|
||||
|
||||
.controller('TimepickerController', ['$scope', '$attrs', '$parse', '$log', '$locale', 'timepickerConfig', function($scope, $attrs, $parse, $log, $locale, timepickerConfig) {
|
||||
var selected = new Date(),
|
||||
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
|
||||
meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
|
||||
|
||||
this.init = function( ngModelCtrl_, inputs ) {
|
||||
ngModelCtrl = ngModelCtrl_;
|
||||
ngModelCtrl.$render = this.render;
|
||||
|
||||
var hoursInputEl = inputs.eq(0),
|
||||
minutesInputEl = inputs.eq(1);
|
||||
|
||||
var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
|
||||
if ( mousewheel ) {
|
||||
this.setupMousewheelEvents( hoursInputEl, minutesInputEl );
|
||||
}
|
||||
|
||||
$scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
|
||||
this.setupInputEvents( hoursInputEl, minutesInputEl );
|
||||
};
|
||||
|
||||
var hourStep = timepickerConfig.hourStep;
|
||||
if ($attrs.hourStep) {
|
||||
$scope.$parent.$watch($parse($attrs.hourStep), function(value) {
|
||||
hourStep = parseInt(value, 10);
|
||||
});
|
||||
}
|
||||
|
||||
var minuteStep = timepickerConfig.minuteStep;
|
||||
if ($attrs.minuteStep) {
|
||||
$scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
|
||||
minuteStep = parseInt(value, 10);
|
||||
});
|
||||
}
|
||||
|
||||
// 12H / 24H mode
|
||||
$scope.showMeridian = timepickerConfig.showMeridian;
|
||||
if ($attrs.showMeridian) {
|
||||
$scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
|
||||
$scope.showMeridian = !!value;
|
||||
|
||||
if ( ngModelCtrl.$error.time ) {
|
||||
// Evaluate from template
|
||||
var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
|
||||
if (angular.isDefined( hours ) && angular.isDefined( minutes )) {
|
||||
selected.setHours( hours );
|
||||
refresh();
|
||||
}
|
||||
} else {
|
||||
updateTemplate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get $scope.hours in 24H mode if valid
|
||||
function getHoursFromTemplate ( ) {
|
||||
var hours = parseInt( $scope.hours, 10 );
|
||||
var valid = ( $scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
|
||||
if ( !valid ) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if ( $scope.showMeridian ) {
|
||||
if ( hours === 12 ) {
|
||||
hours = 0;
|
||||
}
|
||||
if ( $scope.meridian === meridians[1] ) {
|
||||
hours = hours + 12;
|
||||
}
|
||||
}
|
||||
return hours;
|
||||
}
|
||||
|
||||
function getMinutesFromTemplate() {
|
||||
var minutes = parseInt($scope.minutes, 10);
|
||||
return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined;
|
||||
}
|
||||
|
||||
function pad( value ) {
|
||||
return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;
|
||||
}
|
||||
|
||||
// Respond on mousewheel spin
|
||||
this.setupMousewheelEvents = function( hoursInputEl, minutesInputEl ) {
|
||||
var isScrollingUp = function(e) {
|
||||
if (e.originalEvent) {
|
||||
e = e.originalEvent;
|
||||
}
|
||||
//pick correct delta variable depending on event
|
||||
var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;
|
||||
return (e.detail || delta > 0);
|
||||
};
|
||||
|
||||
hoursInputEl.bind('mousewheel wheel', function(e) {
|
||||
$scope.$apply( (isScrollingUp(e)) ? $scope.incrementHours() : $scope.decrementHours() );
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
minutesInputEl.bind('mousewheel wheel', function(e) {
|
||||
$scope.$apply( (isScrollingUp(e)) ? $scope.incrementMinutes() : $scope.decrementMinutes() );
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.setupInputEvents = function( hoursInputEl, minutesInputEl ) {
|
||||
if ( $scope.readonlyInput ) {
|
||||
$scope.updateHours = angular.noop;
|
||||
$scope.updateMinutes = angular.noop;
|
||||
return;
|
||||
}
|
||||
|
||||
var invalidate = function(invalidHours, invalidMinutes) {
|
||||
ngModelCtrl.$setViewValue( null );
|
||||
ngModelCtrl.$setValidity('time', false);
|
||||
if (angular.isDefined(invalidHours)) {
|
||||
$scope.invalidHours = invalidHours;
|
||||
}
|
||||
if (angular.isDefined(invalidMinutes)) {
|
||||
$scope.invalidMinutes = invalidMinutes;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.updateHours = function() {
|
||||
var hours = getHoursFromTemplate();
|
||||
|
||||
if ( angular.isDefined(hours) ) {
|
||||
selected.setHours( hours );
|
||||
refresh( 'h' );
|
||||
} else {
|
||||
invalidate(true);
|
||||
}
|
||||
};
|
||||
|
||||
hoursInputEl.bind('blur', function(e) {
|
||||
if ( !$scope.invalidHours && $scope.hours < 10) {
|
||||
$scope.$apply( function() {
|
||||
$scope.hours = pad( $scope.hours );
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.updateMinutes = function() {
|
||||
var minutes = getMinutesFromTemplate();
|
||||
|
||||
if ( angular.isDefined(minutes) ) {
|
||||
selected.setMinutes( minutes );
|
||||
refresh( 'm' );
|
||||
} else {
|
||||
invalidate(undefined, true);
|
||||
}
|
||||
};
|
||||
|
||||
minutesInputEl.bind('blur', function(e) {
|
||||
if ( !$scope.invalidMinutes && $scope.minutes < 10 ) {
|
||||
$scope.$apply( function() {
|
||||
$scope.minutes = pad( $scope.minutes );
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.render = function() {
|
||||
var date = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : null;
|
||||
|
||||
if ( isNaN(date) ) {
|
||||
ngModelCtrl.$setValidity('time', false);
|
||||
$log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
|
||||
} else {
|
||||
if ( date ) {
|
||||
selected = date;
|
||||
}
|
||||
makeValid();
|
||||
updateTemplate();
|
||||
}
|
||||
};
|
||||
|
||||
// Call internally when we know that model is valid.
|
||||
function refresh( keyboardChange ) {
|
||||
makeValid();
|
||||
ngModelCtrl.$setViewValue( new Date(selected) );
|
||||
updateTemplate( keyboardChange );
|
||||
}
|
||||
|
||||
function makeValid() {
|
||||
ngModelCtrl.$setValidity('time', true);
|
||||
$scope.invalidHours = false;
|
||||
$scope.invalidMinutes = false;
|
||||
}
|
||||
|
||||
function updateTemplate( keyboardChange ) {
|
||||
var hours = selected.getHours(), minutes = selected.getMinutes();
|
||||
|
||||
if ( $scope.showMeridian ) {
|
||||
hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12; // Convert 24 to 12 hour system
|
||||
}
|
||||
|
||||
$scope.hours = keyboardChange === 'h' ? hours : pad(hours);
|
||||
$scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);
|
||||
$scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
|
||||
}
|
||||
|
||||
function addMinutes( minutes ) {
|
||||
var dt = new Date( selected.getTime() + minutes * 60000 );
|
||||
selected.setHours( dt.getHours(), dt.getMinutes() );
|
||||
refresh();
|
||||
}
|
||||
|
||||
$scope.incrementHours = function() {
|
||||
addMinutes( hourStep * 60 );
|
||||
};
|
||||
$scope.decrementHours = function() {
|
||||
addMinutes( - hourStep * 60 );
|
||||
};
|
||||
$scope.incrementMinutes = function() {
|
||||
addMinutes( minuteStep );
|
||||
};
|
||||
$scope.decrementMinutes = function() {
|
||||
addMinutes( - minuteStep );
|
||||
};
|
||||
$scope.toggleMeridian = function() {
|
||||
addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
|
||||
};
|
||||
}])
|
||||
|
||||
.directive('timepicker', function () {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
require: ['timepicker', '?^ngModel'],
|
||||
controller:'TimepickerController',
|
||||
replace: true,
|
||||
scope: {},
|
||||
templateUrl: 'template/timepicker/timepicker.html',
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
||||
|
||||
if ( ngModelCtrl ) {
|
||||
timepickerCtrl.init( ngModelCtrl, element.find('input') );
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
8
src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-html-unsafe-popup.html
Executable file → Normal file
8
src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-html-unsafe-popup.html
Executable file → Normal file
|
@ -1,4 +1,4 @@
|
|||
<div class="bsTooltip {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">
|
||||
<div class="bsTooltip-arrow"></div>
|
||||
<div class="bsTooltip-inner" bind-html-unsafe="content"></div>
|
||||
</div>
|
||||
<div class="bsTooltip {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">
|
||||
<div class="bsTooltip-arrow"></div>
|
||||
<div class="bsTooltip-inner" bind-html-unsafe="content"></div>
|
||||
</div>
|
8
src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-popup.html
Executable file → Normal file
8
src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-popup.html
Executable file → Normal file
|
@ -1,4 +1,4 @@
|
|||
<div class="bsTooltip {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">
|
||||
<div class="bsTooltip-arrow"></div>
|
||||
<div class="bsTooltip-inner" ng-bind="content"></div>
|
||||
</div>
|
||||
<div class="bsTooltip {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">
|
||||
<div class="bsTooltip-arrow"></div>
|
||||
<div class="bsTooltip-inner" ng-bind="content"></div>
|
||||
</div>
|
|
@ -1,9 +1,11 @@
|
|||
import './position';
|
||||
|
||||
/**
|
||||
* The following features are still outstanding: animation as a
|
||||
* function, placement as a function, inside, support for more triggers than
|
||||
* just mouse enter/leave, html tooltips, and selector delegation.
|
||||
*/
|
||||
angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap.bindHtml' ] )
|
||||
angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
|
||||
|
||||
/**
|
||||
* The $tooltip service creates tooltip- and popover-like directives as well as
|
||||
|
@ -345,28 +347,28 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
|
|||
}];
|
||||
})
|
||||
|
||||
.directive( 'tooltipPopup', function () {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
replace: true,
|
||||
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
|
||||
templateUrl: 'template/tooltip/tooltip-popup.html'
|
||||
};
|
||||
})
|
||||
|
||||
.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
|
||||
return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
|
||||
}])
|
||||
|
||||
.directive( 'tooltipHtmlUnsafePopup', function () {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
replace: true,
|
||||
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
|
||||
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
|
||||
};
|
||||
.directive( 'tooltipPopup', function () {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
replace: true,
|
||||
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
|
||||
templateUrl: 'template/tooltip/tooltip-popup.html'
|
||||
};
|
||||
})
|
||||
|
||||
.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
|
||||
return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
|
||||
}]);
|
||||
}])
|
||||
|
||||
.directive( 'tooltipHtmlUnsafePopup', function () {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
replace: true,
|
||||
scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
|
||||
templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
|
||||
};
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
<a tabindex="-1" bind-html-unsafe="match.label | typeaheadHighlight:query"></a>
|
|
@ -1,5 +0,0 @@
|
|||
<ul class="dropdown-menu" ng-show="isOpen()" ng-style="{top: position.top+'px', left: position.left+'px'}" style="display: block;" role="listbox" aria-hidden="{{!isOpen()}}">
|
||||
<li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }" ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)" role="option" id="{{match.id}}">
|
||||
<div typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
|
||||
</li>
|
||||
</ul>
|
|
@ -1,398 +0,0 @@
|
|||
angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])
|
||||
|
||||
/**
|
||||
* A helper service that can parse typeahead's syntax (string provided by users)
|
||||
* Extracted to a separate service for ease of unit testing
|
||||
*/
|
||||
.factory('typeaheadParser', ['$parse', function ($parse) {
|
||||
|
||||
// 00000111000000000000022200000000000000003333333333333330000000000044000
|
||||
var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
|
||||
|
||||
return {
|
||||
parse:function (input) {
|
||||
|
||||
var match = input.match(TYPEAHEAD_REGEXP);
|
||||
if (!match) {
|
||||
throw new Error(
|
||||
'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
|
||||
' but got "' + input + '".');
|
||||
}
|
||||
|
||||
return {
|
||||
itemName:match[3],
|
||||
source:$parse(match[4]),
|
||||
viewMapper:$parse(match[2] || match[1]),
|
||||
modelMapper:$parse(match[1])
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
.directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',
|
||||
function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
|
||||
|
||||
var HOT_KEYS = [9, 13, 27, 38, 40];
|
||||
|
||||
return {
|
||||
require:'ngModel',
|
||||
link:function (originalScope, element, attrs, modelCtrl) {
|
||||
|
||||
//SUPPORTED ATTRIBUTES (OPTIONS)
|
||||
|
||||
//minimal no of characters that needs to be entered before typeahead kicks-in
|
||||
var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
|
||||
|
||||
//minimal wait time after last character typed before typehead kicks-in
|
||||
var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
|
||||
|
||||
//should it restrict model values to the ones selected from the popup only?
|
||||
var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
|
||||
|
||||
//binding to a variable that indicates if matches are being retrieved asynchronously
|
||||
var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
|
||||
|
||||
//a callback executed when a match is selected
|
||||
var onSelectCallback = $parse(attrs.typeaheadOnSelect);
|
||||
|
||||
var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
|
||||
|
||||
var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
|
||||
|
||||
var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
|
||||
|
||||
//INTERNAL VARIABLES
|
||||
|
||||
//model setter executed upon match selection
|
||||
var $setModelValue = $parse(attrs.ngModel).assign;
|
||||
|
||||
//expressions used by typeahead
|
||||
var parserResult = typeaheadParser.parse(attrs.typeahead);
|
||||
|
||||
var hasFocus;
|
||||
|
||||
//create a child scope for the typeahead directive so we are not polluting original scope
|
||||
//with typeahead-specific data (matches, query etc.)
|
||||
var scope = originalScope.$new();
|
||||
originalScope.$on('$destroy', function(){
|
||||
scope.$destroy();
|
||||
});
|
||||
|
||||
// WAI-ARIA
|
||||
var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
|
||||
element.attr({
|
||||
'aria-autocomplete': 'list',
|
||||
'aria-expanded': false,
|
||||
'aria-owns': popupId
|
||||
});
|
||||
|
||||
//pop-up element used to display matches
|
||||
var popUpEl = angular.element('<div typeahead-popup></div>');
|
||||
popUpEl.attr({
|
||||
id: popupId,
|
||||
matches: 'matches',
|
||||
active: 'activeIdx',
|
||||
select: 'select(activeIdx)',
|
||||
query: 'query',
|
||||
position: 'position'
|
||||
});
|
||||
//custom item template
|
||||
if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
|
||||
popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
|
||||
}
|
||||
|
||||
var resetMatches = function() {
|
||||
scope.matches = [];
|
||||
scope.activeIdx = -1;
|
||||
element.attr('aria-expanded', false);
|
||||
};
|
||||
|
||||
var getMatchId = function(index) {
|
||||
return popupId + '-option-' + index;
|
||||
};
|
||||
|
||||
// Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
|
||||
// This attribute is added or removed automatically when the `activeIdx` changes.
|
||||
scope.$watch('activeIdx', function(index) {
|
||||
if (index < 0) {
|
||||
element.removeAttr('aria-activedescendant');
|
||||
} else {
|
||||
element.attr('aria-activedescendant', getMatchId(index));
|
||||
}
|
||||
});
|
||||
|
||||
var getMatchesAsync = function(inputValue) {
|
||||
|
||||
var locals = {$viewValue: inputValue};
|
||||
isLoadingSetter(originalScope, true);
|
||||
$q.when(parserResult.source(originalScope, locals)).then(function(matches) {
|
||||
|
||||
//it might happen that several async queries were in progress if a user were typing fast
|
||||
//but we are interested only in responses that correspond to the current view value
|
||||
var onCurrentRequest = (inputValue === modelCtrl.$viewValue);
|
||||
if (onCurrentRequest && hasFocus) {
|
||||
if (matches.length > 0) {
|
||||
|
||||
scope.activeIdx = focusFirst ? 0 : -1;
|
||||
scope.matches.length = 0;
|
||||
|
||||
//transform labels
|
||||
for(var i=0; i<matches.length; i++) {
|
||||
locals[parserResult.itemName] = matches[i];
|
||||
scope.matches.push({
|
||||
id: getMatchId(i),
|
||||
label: parserResult.viewMapper(scope, locals),
|
||||
model: matches[i]
|
||||
});
|
||||
}
|
||||
|
||||
scope.query = inputValue;
|
||||
//position pop-up with matches - we need to re-calculate its position each time we are opening a window
|
||||
//with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
|
||||
//due to other elements being rendered
|
||||
scope.position = appendToBody ? $position.offset(element) : $position.position(element);
|
||||
scope.position.top = scope.position.top + element.prop('offsetHeight');
|
||||
|
||||
element.attr('aria-expanded', true);
|
||||
} else {
|
||||
resetMatches();
|
||||
}
|
||||
}
|
||||
if (onCurrentRequest) {
|
||||
isLoadingSetter(originalScope, false);
|
||||
}
|
||||
}, function(){
|
||||
resetMatches();
|
||||
isLoadingSetter(originalScope, false);
|
||||
});
|
||||
};
|
||||
|
||||
resetMatches();
|
||||
|
||||
//we need to propagate user's query so we can highlight matches
|
||||
scope.query = undefined;
|
||||
|
||||
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
|
||||
var timeoutPromise;
|
||||
|
||||
var scheduleSearchWithTimeout = function(inputValue) {
|
||||
timeoutPromise = $timeout(function () {
|
||||
getMatchesAsync(inputValue);
|
||||
}, waitTime);
|
||||
};
|
||||
|
||||
var cancelPreviousTimeout = function() {
|
||||
if (timeoutPromise) {
|
||||
$timeout.cancel(timeoutPromise);
|
||||
}
|
||||
};
|
||||
|
||||
//plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
|
||||
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
|
||||
modelCtrl.$parsers.unshift(function (inputValue) {
|
||||
|
||||
hasFocus = true;
|
||||
|
||||
if (inputValue && inputValue.length >= minSearch) {
|
||||
if (waitTime > 0) {
|
||||
cancelPreviousTimeout();
|
||||
scheduleSearchWithTimeout(inputValue);
|
||||
} else {
|
||||
getMatchesAsync(inputValue);
|
||||
}
|
||||
} else {
|
||||
isLoadingSetter(originalScope, false);
|
||||
cancelPreviousTimeout();
|
||||
resetMatches();
|
||||
}
|
||||
|
||||
if (isEditable) {
|
||||
return inputValue;
|
||||
} else {
|
||||
if (!inputValue) {
|
||||
// Reset in case user had typed something previously.
|
||||
modelCtrl.$setValidity('editable', true);
|
||||
return inputValue;
|
||||
} else {
|
||||
modelCtrl.$setValidity('editable', false);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modelCtrl.$formatters.push(function (modelValue) {
|
||||
|
||||
var candidateViewValue, emptyViewValue;
|
||||
var locals = {};
|
||||
|
||||
if (inputFormatter) {
|
||||
|
||||
locals.$model = modelValue;
|
||||
return inputFormatter(originalScope, locals);
|
||||
|
||||
} else {
|
||||
|
||||
//it might happen that we don't have enough info to properly render input value
|
||||
//we need to check for this situation and simply return model value if we can't apply custom formatting
|
||||
locals[parserResult.itemName] = modelValue;
|
||||
candidateViewValue = parserResult.viewMapper(originalScope, locals);
|
||||
locals[parserResult.itemName] = undefined;
|
||||
emptyViewValue = parserResult.viewMapper(originalScope, locals);
|
||||
|
||||
return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;
|
||||
}
|
||||
});
|
||||
|
||||
scope.select = function (activeIdx) {
|
||||
//called from within the $digest() cycle
|
||||
var locals = {};
|
||||
var model, item;
|
||||
|
||||
locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
|
||||
model = parserResult.modelMapper(originalScope, locals);
|
||||
$setModelValue(originalScope, model);
|
||||
modelCtrl.$setValidity('editable', true);
|
||||
|
||||
onSelectCallback(originalScope, {
|
||||
$item: item,
|
||||
$model: model,
|
||||
$label: parserResult.viewMapper(originalScope, locals)
|
||||
});
|
||||
|
||||
resetMatches();
|
||||
|
||||
//return focus to the input element if a match was selected via a mouse click event
|
||||
// use timeout to avoid $rootScope:inprog error
|
||||
$timeout(function() { element[0].focus(); }, 0, false);
|
||||
};
|
||||
|
||||
//bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
|
||||
element.bind('keydown', function (evt) {
|
||||
|
||||
//typeahead is open and an "interesting" key was pressed
|
||||
if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if there's nothing selected (i.e. focusFirst) and enter is hit, don't do anything
|
||||
if (scope.activeIdx == -1 && (evt.which === 13 || evt.which === 9)) {
|
||||
return;
|
||||
}
|
||||
|
||||
evt.preventDefault();
|
||||
|
||||
if (evt.which === 40) {
|
||||
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
|
||||
scope.$digest();
|
||||
|
||||
} else if (evt.which === 38) {
|
||||
scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
|
||||
scope.$digest();
|
||||
|
||||
} else if (evt.which === 13 || evt.which === 9) {
|
||||
scope.$apply(function () {
|
||||
scope.select(scope.activeIdx);
|
||||
});
|
||||
|
||||
} else if (evt.which === 27) {
|
||||
evt.stopPropagation();
|
||||
|
||||
resetMatches();
|
||||
scope.$digest();
|
||||
}
|
||||
});
|
||||
|
||||
element.bind('blur', function (evt) {
|
||||
hasFocus = false;
|
||||
});
|
||||
|
||||
// Keep reference to click handler to unbind it.
|
||||
var dismissClickHandler = function (evt) {
|
||||
if (element[0] !== evt.target) {
|
||||
resetMatches();
|
||||
scope.$digest();
|
||||
}
|
||||
};
|
||||
|
||||
$document.bind('click', dismissClickHandler);
|
||||
|
||||
originalScope.$on('$destroy', function(){
|
||||
$document.unbind('click', dismissClickHandler);
|
||||
if (appendToBody) {
|
||||
$popup.remove();
|
||||
}
|
||||
});
|
||||
|
||||
var $popup = $compile(popUpEl)(scope);
|
||||
if (appendToBody) {
|
||||
$document.find('body').append($popup);
|
||||
} else {
|
||||
element.after($popup);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}])
|
||||
|
||||
.directive('typeaheadPopup', function () {
|
||||
return {
|
||||
restrict:'EA',
|
||||
scope:{
|
||||
matches:'=',
|
||||
query:'=',
|
||||
active:'=',
|
||||
position:'=',
|
||||
select:'&'
|
||||
},
|
||||
replace:true,
|
||||
templateUrl:'template/typeahead/typeahead-popup.html',
|
||||
link:function (scope, element, attrs) {
|
||||
|
||||
scope.templateUrl = attrs.templateUrl;
|
||||
|
||||
scope.isOpen = function () {
|
||||
return scope.matches.length > 0;
|
||||
};
|
||||
|
||||
scope.isActive = function (matchIdx) {
|
||||
return scope.active == matchIdx;
|
||||
};
|
||||
|
||||
scope.selectActive = function (matchIdx) {
|
||||
scope.active = matchIdx;
|
||||
};
|
||||
|
||||
scope.selectMatch = function (activeIdx) {
|
||||
scope.select({activeIdx:activeIdx});
|
||||
};
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
.directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) {
|
||||
return {
|
||||
restrict:'EA',
|
||||
scope:{
|
||||
index:'=',
|
||||
match:'=',
|
||||
query:'='
|
||||
},
|
||||
link:function (scope, element, attrs) {
|
||||
var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';
|
||||
$http.get(tplUrl, {cache: $templateCache}).then(function(resp){
|
||||
element.replaceWith($compile(resp.data.trim())(scope));
|
||||
});
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
.filter('typeaheadHighlight', function() {
|
||||
|
||||
function escapeRegexp(queryToEscape) {
|
||||
return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
|
||||
}
|
||||
|
||||
return function(matchItem, query) {
|
||||
return query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem;
|
||||
};
|
||||
});
|
|
@ -264,13 +264,6 @@
|
|||
"common.ui.aggTypes.sortOnTooltip": "排序依据",
|
||||
"common.ui.aggTypes.timeIntervalLabel": "时间间隔",
|
||||
"common.ui.aggTypes.valuesLabel": "值",
|
||||
"common.ui.angularBootstrap.alert.closeButtonLabel": "关闭",
|
||||
"common.ui.angularBootstrap.pagination.pagerConfig.nextLabel": "下一页 »",
|
||||
"common.ui.angularBootstrap.pagination.pagerConfig.previousLabel": "« 上一页",
|
||||
"common.ui.angularBootstrap.pagination.paginationConfig.firstLabel": "第一",
|
||||
"common.ui.angularBootstrap.pagination.paginationConfig.lastLabel": "最后",
|
||||
"common.ui.angularBootstrap.pagination.paginationConfig.nextLabel": "下一个",
|
||||
"common.ui.angularBootstrap.pagination.paginationConfig.previousLabel": "上一个",
|
||||
"common.ui.chrome.bigUrlWarningNotificationMessage": "在“{advancedSettingsLink}”启用“{storeInSessionStorageParam}”选项,或简化屏幕视觉效果。",
|
||||
"common.ui.chrome.bigUrlWarningNotificationMessage.advancedSettingsLinkText": "高级设置",
|
||||
"common.ui.chrome.bigUrlWarningNotificationTitle": "URL 过大,Kibana 可能无法工作",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue