Merge ~deadlight/maas:sticky-header-height-fix into maas:master

Proposed by Karl Williams
Status: Merged
Approved by: Andres Rodriguez
Approved revision: 0fb98a7194f35db8b44d808b475b930f1e519fba
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~deadlight/maas:sticky-header-height-fix
Merge into: maas:master
Diff against target: 1418 lines (+3/-688)
4 files modified
dev/null (+0/-684)
src/maasserver/static/js/angular/maas.js (+1/-2)
src/maasserver/static/partials/node-details.html (+1/-1)
src/maasserver/static/partials/node-events.html (+1/-1)
Reviewer Review Type Date Requested Status
Andres Rodriguez (community) Approve
Review via email: mp+340851@code.launchpad.net

Commit message

Remove sticky header javascript

Removed instances of the sticky javascript and the source files

Description of the change

Remove sticky header javascript

Removed instances of the sticky javascript and the source files

QA:
 - Go to a machine details page
 - Select an action in the header
 - Scroll the page and see that the header background is the correct size/colour

To post a comment you must log in.
Revision history for this message
Andres Rodriguez (andreserl) wrote :

lgtm!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/static/js/angular/3rdparty/sticky.js b/src/maasserver/static/js/angular/3rdparty/sticky.js
2deleted file mode 100644
3index 977c232..0000000
4--- a/src/maasserver/static/js/angular/3rdparty/sticky.js
5+++ /dev/null
6@@ -1,682 +0,0 @@
7-/**
8- * ngSticky - https://github.com/d-oliveros/ngSticky
9- *
10- * A simple, pure javascript (No jQuery required!) AngularJS directive
11- * to make elements stick when scrolling down.
12- *
13- * Credits: https://github.com/d-oliveros/ngSticky/graphs/contributors
14- */
15-(function() {
16- 'use strict';
17-
18- var module = angular.module('sticky', []);
19-
20- /**
21- * Directive: sticky
22- */
23- module.directive('sticky', ['$window', '$timeout', function($window, $timeout) {
24- return {
25- restrict: 'A', // this directive can only be used as an attribute.
26- scope: {
27- disabled: '=disabledSticky'
28- },
29- link: function linkFn($scope, $elem, $attrs) {
30-
31- // Initial scope
32- var scrollableNodeTagName = 'sticky-scroll';
33- var initialPosition = $elem.css('position');
34- var initialStyle = $elem.attr('style') || '';
35- var stickyBottomLine = 0;
36- var isSticking = false;
37- var onStickyHeighUnbind;
38- var originalInitialCSS;
39- var originalOffset;
40- var placeholder;
41- var stickyLine;
42- var initialCSS;
43-
44- // Optional Classes
45- var stickyClass = $attrs.stickyClass || '';
46- var unstickyClass = $attrs.unstickyClass || '';
47- var bodyClass = $attrs.bodyClass || '';
48- var bottomClass = $attrs.bottomClass || '';
49-
50- // Find scrollbar
51- var scrollbar = deriveScrollingViewport ($elem);
52-
53- // Define elements
54- var windowElement = angular.element($window);
55- var scrollbarElement = angular.element(scrollbar);
56- var $body = angular.element(document.body);
57-
58- // Resize callback
59- var $onResize = function () {
60- if ($scope.$root && !$scope.$root.$$phase) {
61- $scope.$apply(onResize);
62- } else {
63- onResize();
64- }
65- };
66-
67- // Define options
68- var usePlaceholder = ($attrs.usePlaceholder !== 'false');
69- var anchor = $attrs.anchor === 'bottom' ? 'bottom' : 'top';
70- var confine = ($attrs.confine === 'true');
71-
72- // flag: can react to recalculating the initial CSS dimensions later
73- // as link executes prematurely. defaults to immediate checking
74- var isStickyLayoutDeferred = $attrs.isStickyLayoutDeferred !== undefined
75- ? ($attrs.isStickyLayoutDeferred === 'true')
76- : false;
77-
78- // flag: is sticky content constantly observed for changes.
79- // Should be true if content uses ngBind to show text
80- // that may vary in size over time
81- var isStickyLayoutWatched = $attrs.isStickyLayoutWatched !== undefined
82- ? ($attrs.isStickyLayoutWatched === 'true')
83- : true;
84-
85-
86- var offset = $attrs.offset
87- ? parseInt ($attrs.offset.replace(/px;?/, ''))
88- : 0;
89-
90- /**
91- * Trigger to initialize the sticky
92- * Because of the `timeout()` method for the call of
93- * @type {Boolean}
94- */
95- var shouldInitialize = true;
96-
97- /**
98- * Initialize Sticky
99- */
100- function initSticky() {
101-
102- if (shouldInitialize) {
103-
104- // Listeners
105- scrollbarElement.on('scroll', checkIfShouldStick);
106- windowElement.on('resize', $onResize);
107-
108- memorizeDimensions(); // remember sticky's layout dimensions
109-
110- // Setup watcher on digest and change
111- $scope.$watch(onDigest, onChange);
112-
113- // Clean up
114- $scope.$on('$destroy', onDestroy);
115- shouldInitialize = false;
116- }
117- };
118-
119- /**
120- * need to recall sticky's DOM attributes (make sure layout has occured)
121- */
122- function memorizeDimensions() {
123- // immediate assignment, but there is the potential for wrong values if content not ready
124- initialCSS = $scope.getInitialDimensions();
125-
126- // option to calculate the dimensions when layout is 'ready'
127- if (isStickyLayoutDeferred) {
128-
129- // logic: when this directive link() runs before the content has had a chance to layout on browser, height could be 0
130- if (!$elem[0].getBoundingClientRect().height) {
131-
132- onStickyHeighUnbind = $scope.$watch(
133- function() {
134- return $elem.height();
135- },
136-
137- // state change: sticky content's height set
138- function onStickyContentLayoutInitialHeightSet(newValue, oldValue) {
139- if (newValue > 0) {
140- // now can memorize
141- initialCSS = $scope.getInitialDimensions();
142-
143- if (!isStickyLayoutWatched) {
144- // preference was to do just a one-time async watch on the sticky's content; now stop watching
145- onStickyHeighUnbind();
146- }
147- }
148- }
149- );
150- }
151- }
152- }
153-
154- /**
155- * Determine if the element should be sticking or not.
156- */
157- var checkIfShouldStick = function() {
158- if ($scope.disabled === true || mediaQueryMatches()) {
159- if (isSticking) unStickElement();
160- return false;
161- }
162-
163- // What's the document client top for?
164- var scrollbarPosition = scrollbarYPos();
165- var shouldStick;
166-
167- if (anchor === 'top') {
168- if (confine === true) {
169- shouldStick = scrollbarPosition > stickyLine && scrollbarPosition <= stickyBottomLine;
170- } else {
171- shouldStick = scrollbarPosition > stickyLine;
172- }
173- } else {
174- shouldStick = scrollbarPosition <= stickyLine;
175- }
176-
177- // Switch the sticky mode if the element crosses the sticky line
178- // $attrs.stickLimit - when it's equal to true it enables the user
179- // to turn off the sticky function when the elem height is
180- // bigger then the viewport
181- var closestLine = getClosest (scrollbarPosition, stickyLine, stickyBottomLine);
182-
183- if (shouldStick && !shouldStickWithLimit ($attrs.stickLimit) && !isSticking) {
184- stickElement (closestLine);
185- } else if (!shouldStick && isSticking) {
186- unStickElement(closestLine, scrollbarPosition);
187- } else if (confine && !shouldStick) {
188- // If we are confined to the parent, refresh, and past the stickyBottomLine
189- // We should 'remember' the original offset and unstick the element which places it at the stickyBottomLine
190- originalOffset = elementsOffsetFromTop ($elem[0]);
191- unStickElement (closestLine, scrollbarPosition);
192- }
193- };
194-
195- /**
196- * determine the respective node that handles scrolling, defaulting to browser window
197- */
198- function deriveScrollingViewport(stickyNode) {
199- // derive relevant scrolling by ascending the DOM tree
200- var match =findAncestorTag (scrollableNodeTagName, stickyNode);
201- return (match.length === 1) ? match[0] : $window;
202- }
203-
204- /**
205- * since jqLite lacks closest(), this is a pseudo emulator (by tag name)
206- */
207- function findAncestorTag(tag, context) {
208- var m = []; // nodelist container
209- var n = context.parent(); // starting point
210- var p;
211-
212- do {
213- var node = n[0]; // break out of jqLite
214- // limit DOM territory
215- if (node.nodeType !== 1) {
216- break;
217- }
218-
219- // success
220- if (node.tagName.toUpperCase() === tag.toUpperCase()) {
221- return n;
222- }
223-
224- p = n.parent();
225- n = p; // set to parent
226- } while (p.length !== 0);
227-
228- return m; // empty set
229- }
230-
231- /**
232- * Seems to be undocumented functionality
233- */
234- function shouldStickWithLimit(shouldApplyWithLimit) {
235- return shouldApplyWithLimit === 'true'
236- ? ($window.innerHeight - ($elem[0].offsetHeight + parseInt(offset)) < 0)
237- : false;
238- }
239-
240- /**
241- * Finds the closest value from a set of numbers in an array.
242- */
243- function getClosest(scrollTop, stickyLine, stickyBottomLine) {
244- var closest = 'top';
245- var topDistance = Math.abs(scrollTop - stickyLine);
246- var bottomDistance = Math.abs(scrollTop - stickyBottomLine);
247-
248- if (topDistance > bottomDistance) {
249- closest = 'bottom';
250- }
251-
252- return closest;
253- }
254-
255- /**
256- * Unsticks the element
257- */
258- function unStickElement(fromDirection) {
259- if (initialStyle) {
260- $elem.attr('style', initialStyle);
261- }
262- isSticking = false;
263-
264- initialCSS.width = $scope.getInitialDimensions().width;
265-
266- $body.removeClass(bodyClass);
267- $elem.removeClass(stickyClass);
268- $elem.addClass(unstickyClass);
269-
270- if (fromDirection === 'top') {
271- $elem.removeClass(bottomClass);
272-
273- $elem
274- .css('z-index', 10)
275- .css('width', initialCSS.width)
276- .css('top', initialCSS.top)
277- .css('position', initialCSS.position)
278- .css('left', initialCSS.cssLeft)
279- .css('margin-top', initialCSS.marginTop);
280- } else if (fromDirection === 'bottom' && confine === true) {
281- $elem.addClass(bottomClass);
282-
283- // It's possible to page down page and skip the 'stickElement'.
284- // In that case we should create a placeholder so the offsets don't get off.
285- createPlaceholder();
286-
287- $elem
288- .css('z-index', 10)
289- .css('width', initialCSS.width)
290- .css('top', '')
291- .css('bottom', 0)
292- .css('position', 'absolute')
293- .css('left', initialCSS.cssLeft)
294- .css('margin-top', initialCSS.marginTop)
295- .css('margin-bottom', initialCSS.marginBottom);
296- }
297-
298- if (placeholder && fromDirection === anchor) {
299- placeholder.remove();
300- }
301- }
302-
303- /**
304- * Sticks the element
305- */
306- function stickElement(closestLine) {
307- // Set sticky state
308- isSticking = true;
309- $timeout(function() {
310- initialCSS.offsetWidth = $elem[0].offsetWidth;
311- }, 0);
312- $body.addClass(bodyClass);
313- $elem.removeClass(unstickyClass);
314- $elem.removeClass(bottomClass);
315- $elem.addClass(stickyClass);
316-
317- createPlaceholder();
318-
319- $elem
320- .css('z-index', '10')
321- .css('width', $elem[0].offsetWidth + 'px')
322- .css('position', 'fixed')
323- .css('left', $elem.css('left').replace('px', '') + 'px')
324- .css(anchor, (offset + elementsOffsetFromTop (scrollbar)) + 'px')
325- .css('margin-top', 0);
326-
327- if (anchor === 'bottom') {
328- $elem.css('margin-bottom', 0);
329- }
330- }
331-
332- /**
333- * Clean up directive
334- */
335- var onDestroy = function() {
336- scrollbarElement.off('scroll', checkIfShouldStick);
337- windowElement.off('resize', $onResize);
338-
339- $onResize = null;
340-
341- $body.removeClass(bodyClass);
342-
343- if (placeholder) {
344- placeholder.remove();
345- }
346- };
347-
348- /**
349- * Updates on resize.
350- */
351- function onResize() {
352- unStickElement (anchor);
353- checkIfShouldStick();
354- }
355-
356- /**
357- * Triggered on load / digest cycle
358- * return `0` if the DOM element is hidden
359- */
360- var onDigest = function() {
361- if ($scope.disabled === true) {
362- return unStickElement();
363- }
364- var offsetFromTop = elementsOffsetFromTop ($elem[0]);
365- if (offsetFromTop === 0) {
366- return offsetFromTop;
367- }
368- if (anchor === 'top') {
369- return (originalOffset || offsetFromTop) - elementsOffsetFromTop (scrollbar) + scrollbarYPos();
370- } else {
371- return offsetFromTop - scrollbarHeight() + $elem[0].offsetHeight + scrollbarYPos();
372- }
373- };
374-
375- /**
376- * Triggered on change
377- */
378- var onChange = function (newVal, oldVal) {
379-
380- /**
381- * Indicate if the DOM element is showed, or not
382- * @type {boolean}
383- */
384- var elemIsShowed = !!newVal;
385-
386- /**
387- * Indicate if the DOM element was showed, or not
388- * @type {boolean}
389- */
390- var elemWasHidden = !oldVal;
391- var valChange = (newVal !== oldVal || typeof stickyLine === 'undefined');
392- var notSticking = (!isSticking && !isBottomedOut());
393-
394- if (valChange && notSticking && newVal > 0 && elemIsShowed) {
395- stickyLine = newVal - offset;
396- //Update dimensions of sticky element when is showed
397- if (elemIsShowed && elemWasHidden) {
398- $scope.updateStickyContentUpdateDimensions($elem[0].offsetWidth, $elem[0].offsetHeight);
399- }
400- // IF the sticky is confined, we want to make sure the parent is relatively positioned,
401- // otherwise it won't bottom out properly
402- if (confine) {
403- $elem.parent().css({
404- 'position': 'relative'
405- });
406- }
407-
408- // Get Parent height, so we know when to bottom out for confined stickies
409- var parent = $elem.parent()[0];
410-
411- // Offset parent height by the elements height, if we're not using a placeholder
412- var parentHeight = parseInt (parent.offsetHeight) - (usePlaceholder ? 0 : $elem[0].offsetHeight);
413-
414- // and now lets ensure we adhere to the bottom margins
415- // TODO: make this an attribute? Maybe like ignore-margin?
416- var marginBottom = parseInt ($elem.css('margin-bottom').replace(/px;?/, '')) || 0;
417-
418- // specify the bottom out line for the sticky to unstick
419- var elementsDistanceFromTop = elementsOffsetFromTop ($elem[0]);
420- var parentsDistanceFromTop = elementsOffsetFromTop (parent)
421- var scrollbarDistanceFromTop = elementsOffsetFromTop (scrollbar);
422-
423- var elementsDistanceFromScrollbarStart = elementsDistanceFromTop - scrollbarDistanceFromTop;
424- var elementsDistanceFromBottom = parentsDistanceFromTop + parentHeight - elementsDistanceFromTop;
425-
426- stickyBottomLine = elementsDistanceFromScrollbarStart
427- + elementsDistanceFromBottom
428- - $elem[0].offsetHeight
429- - marginBottom
430- - offset
431- + +scrollbarYPos();
432-
433- checkIfShouldStick();
434- }
435- };
436-
437- /**
438- * Helper Functions
439- */
440-
441- /**
442- * Create a placeholder
443- */
444- function createPlaceholder() {
445- if (usePlaceholder) {
446- // Remove the previous placeholder
447- if (placeholder) {
448- placeholder.remove();
449- }
450-
451- placeholder = angular.element('<div>');
452- var elementsHeight = $elem[0].offsetHeight;
453- var computedStyle = $elem[0].currentStyle || window.getComputedStyle($elem[0]);
454- elementsHeight += parseInt(computedStyle.marginTop, 10);
455- elementsHeight += parseInt(computedStyle.marginBottom, 10);
456- elementsHeight += parseInt(computedStyle.borderTopWidth, 10);
457- elementsHeight += parseInt(computedStyle.borderBottomWidth, 10);
458- placeholder.css('height', $elem[0].offsetHeight + 'px');
459-
460- $elem.after(placeholder);
461- }
462- }
463-
464- /**
465- * Are we bottomed out of the parent element?
466- */
467- function isBottomedOut() {
468- if (confine && scrollbarYPos() > stickyBottomLine) {
469- return true;
470- }
471-
472- return false;
473- }
474-
475- /**
476- * Fetch top offset of element
477- */
478- function elementsOffsetFromTop(element) {
479- var offset = 0;
480-
481- if (element.getBoundingClientRect) {
482- offset = element.getBoundingClientRect().top;
483- }
484-
485- return offset;
486- }
487-
488- /**
489- * Retrieves top scroll distance
490- */
491- function scrollbarYPos() {
492- var position;
493-
494- if (typeof scrollbar.scrollTop !== 'undefined') {
495- position = scrollbar.scrollTop;
496- } else if (typeof scrollbar.pageYOffset !== 'undefined') {
497- position = scrollbar.pageYOffset;
498- } else {
499- position = document.documentElement.scrollTop;
500- }
501-
502- return position;
503- }
504-
505- /**
506- * Determine scrollbar's height
507- */
508- function scrollbarHeight() {
509- var height;
510-
511- if (scrollbarElement[0] instanceof HTMLElement) {
512- // isn't bounding client rect cleaner than insane regex mess?
513- height = $window.getComputedStyle(scrollbarElement[0], null)
514- .getPropertyValue('height')
515- .replace(/px;?/, '');
516- } else {
517- height = $window.innerHeight;
518- }
519-
520- return parseInt (height) || 0;
521- }
522-
523- /**
524- * Checks if the media matches
525- */
526- function mediaQueryMatches() {
527- var mediaQuery = $attrs.mediaQuery || false;
528- var matchMedia = $window.matchMedia;
529-
530- return mediaQuery && !(matchMedia ('(' + mediaQuery + ')').matches || matchMedia (mediaQuery).matches);
531- }
532-
533- /**
534- * Get more accurate CSS values
535- */
536- function getCSS($el, prop){
537- var el = $el[0],
538- computed = window.getComputedStyle(el),
539- prevDisplay = computed.display,
540- val;
541-
542- // hide the element so that we can get original css
543- // values instead of computed values
544- el.style.display = "none";
545-
546- // NOTE - computed style declaration object is a reference
547- // to the element's CSSStyleDeclaration, so it will always
548- // reflect the current style of the element
549- val = computed[prop];
550-
551- // restore previous display value
552- el.style.display = prevDisplay;
553-
554- return val;
555- }
556-
557- // public accessors for the controller to hitch into. Helps with external API access
558- $scope.getElement = function() { return $elem; };
559- $scope.getScrollbar = function() { return scrollbar; };
560- $scope.getInitialCSS = function() { return initialCSS; };
561- $scope.getAnchor = function() { return anchor; };
562- $scope.isSticking = function() { return isSticking; };
563- $scope.getOriginalInitialCSS = function() { return originalInitialCSS; };
564- // pass through aliases
565- $scope.processUnStickElement = function(anchor) { unStickElement(anchor)};
566- $scope.processCheckIfShouldStick =function() { checkIfShouldStick(); };
567-
568- /**
569- * set the dimensions for the defaults of the content block occupied by the sticky element
570- */
571- $scope.getInitialDimensions = function() {
572- return {
573- zIndex: $elem.css('z-index'),
574- top: $elem.css('top'),
575- position: initialPosition, // revert to true initial state
576- marginTop: $elem.css('margin-top'),
577- marginBottom: $elem.css('margin-bottom'),
578- cssLeft: getCSS($elem, 'left'),
579- width: $elem[0].offsetWidth,
580- height: $elem.css('height')
581- };
582- };
583-
584- /**
585- * only change content box dimensions
586- */
587- $scope.updateStickyContentUpdateDimensions = function(width, height) {
588- if (width && height) {
589- initSticky();
590- initialCSS.width = width + 'px';
591- initialCSS.height = height + 'px';
592- }
593- };
594-
595- // ----------- configuration -----------
596-
597- $timeout(function() {
598- originalInitialCSS = $scope.getInitialDimensions(); // preserve a copy
599- // Init the directive
600- initSticky();
601- },0);
602- },
603-
604- /**
605- * +++++++++ public APIs+++++++++++++
606- */
607- controller: ['$scope', '$window', function($scope, $window) {
608-
609- /**
610- * integration method allows for an outside client to reset the pinned state back to unpinned.
611- * Useful for when refreshing the scrollable DIV content completely
612- * if newWidth and newHeight integer values are not supplied then function will make a best guess
613- */
614- this.resetLayout = function(newWidth, newHeight) {
615-
616- var scrollbar = $scope.getScrollbar(),
617- initialCSS = $scope.getInitialCSS(),
618- anchor = $scope.getAnchor();
619-
620- function _resetScrollPosition() {
621-
622- // reset means content is scrolled to anchor position
623- if (anchor === 'top') {
624- // window based scroller
625- if (scrollbar === $window) {
626- $window.scrollTo(0, 0);
627- // DIV based sticky scroller
628- } else {
629- if (scrollbar.scrollTop > 0) {
630- scrollbar.scrollTop = 0;
631- }
632- }
633- }
634- // todo: need bottom use case
635- }
636-
637- // only if pinned, force unpinning, otherwise height is inadvertently reset to 0
638- if ($scope.isSticking()) {
639- $scope.processUnStickElement (anchor);
640- $scope.processCheckIfShouldStick();
641- }
642- // remove layout-affecting attribures that were modified by this sticky
643- $scope.getElement().css({ 'width': '', 'height': '', 'position': '', 'top': '', zIndex: '' });
644- // model resets
645- initialCSS.position = $scope.getOriginalInitialCSS().position; // revert to original state
646- delete initialCSS.offsetWidth; // stickElement affected
647-
648- // use this directive element's as default, if no measurements passed in
649- if (newWidth === undefined && newHeight === undefined) {
650- var e_bcr = $scope.getElement()[0].getBoundingClientRect();
651- newWidth = e_bcr.width;
652- newHeight = e_bcr.height;
653- }
654-
655- // update model with new dimensions (if supplied from client's own measurement)
656- $scope.updateStickyContentUpdateDimensions(newWidth, newHeight); // update layout dimensions only
657-
658- _resetScrollPosition();
659- };
660-
661- /**
662- * return a reference to the scrolling element (window or DIV with overflow)
663- */
664- this.getScrollbar = function() {
665- return $scope.getScrollbar();
666- };
667- }]
668- };
669- }]
670- );
671-
672- // Shiv: matchMedia
673- window.matchMedia = window.matchMedia || (function() {
674- var warning = 'angular-sticky: This browser does not support ' +
675- 'matchMedia, therefore the minWidth option will not work on ' +
676- 'this browser. Polyfill matchMedia to fix this issue.';
677-
678- if (window.console && console.warn) {
679- console.warn(warning);
680- }
681-
682- return function() {
683- return {
684- matches: true
685- };
686- };
687- }());
688-}());
689diff --git a/src/maasserver/static/js/angular/3rdparty/sticky.min.js b/src/maasserver/static/js/angular/3rdparty/sticky.min.js
690deleted file mode 100644
691index d8ae3da..0000000
692--- a/src/maasserver/static/js/angular/3rdparty/sticky.min.js
693+++ /dev/null
694@@ -1,684 +0,0 @@
695-/**
696- * ngSticky - https://github.com/d-oliveros/ngSticky
697- *
698- * A simple, pure javascript (No jQuery required!) AngularJS directive
699- * to make elements stick when scrolling down.
700- *
701- * Credits: https://github.com/d-oliveros/ngSticky/graphs/contributors
702- */
703-(function() {
704- 'use strict';
705-
706- var module = angular.module('sticky', []);
707-
708- /**
709- * Directive: sticky
710- */
711- module.directive('sticky', ['$window', '$timeout', function($window, $timeout) {
712- return {
713- restrict: 'A', // this directive can only be used as an attribute.
714- scope: {
715- disabled: '=disabledSticky'
716- },
717- link: function linkFn($scope, $elem, $attrs) {
718-
719- // Initial scope
720- var scrollableNodeTagName = 'sticky-scroll';
721- var initialPosition = $elem.css('position');
722- var initialStyle = $elem.attr('style') || '';
723- var stickyBottomLine = 0;
724- var isSticking = false;
725- var onStickyHeighUnbind;
726- var originalInitialCSS;
727- var originalOffset;
728- var placeholder;
729- var stickyLine;
730- var initialCSS;
731-
732- // Optional Classes
733- var stickyClass = $attrs.stickyClass || '';
734- var unstickyClass = $attrs.unstickyClass || '';
735- var bodyClass = $attrs.bodyClass || '';
736- var bottomClass = $attrs.bottomClass || '';
737-
738- // Find scrollbar
739- var scrollbar = deriveScrollingViewport ($elem);
740-
741- // Define elements
742- var windowElement = angular.element($window);
743- var scrollbarElement = angular.element(scrollbar);
744- var $body = angular.element(document.body);
745-
746- // Resize callback
747- var $onResize = function () {
748- if ($scope.$root && !$scope.$root.$$phase) {
749- $scope.$apply(onResize);
750- } else {
751- onResize();
752- }
753- };
754-
755- // Define options
756- var usePlaceholder = ($attrs.usePlaceholder !== 'false');
757- var anchor = $attrs.anchor === 'bottom' ? 'bottom' : 'top';
758- var confine = ($attrs.confine === 'true');
759-
760- // flag: can react to recalculating the initial CSS dimensions later
761- // as link executes prematurely. defaults to immediate checking
762- var isStickyLayoutDeferred = $attrs.isStickyLayoutDeferred !== undefined
763- ? ($attrs.isStickyLayoutDeferred === 'true')
764- : false;
765-
766- // flag: is sticky content constantly observed for changes.
767- // Should be true if content uses ngBind to show text
768- // that may vary in size over time
769- var isStickyLayoutWatched = $attrs.isStickyLayoutWatched !== undefined
770- ? ($attrs.isStickyLayoutWatched === 'true')
771- : true;
772-
773-
774- var offset = $attrs.offset
775- ? parseInt ($attrs.offset.replace(/px;?/, ''))
776- : 0;
777-
778- /**
779- * Trigger to initialize the sticky
780- * Because of the `timeout()` method for the call of
781- * @type {Boolean}
782- */
783- var shouldInitialize = true;
784-
785- /**
786- * Initialize Sticky
787- */
788- function initSticky() {
789-
790- if (shouldInitialize) {
791-
792- // Listeners
793- scrollbarElement.on('scroll', checkIfShouldStick);
794- windowElement.on('resize', $onResize);
795-
796- memorizeDimensions(); // remember sticky's layout dimensions
797-
798- // Setup watcher on digest and change
799- $scope.$watch(onDigest, onChange);
800-
801- // Clean up
802- $scope.$on('$destroy', onDestroy);
803- shouldInitialize = false;
804- }
805- };
806-
807- /**
808- * need to recall sticky's DOM attributes (make sure layout has occured)
809- */
810- function memorizeDimensions() {
811- // immediate assignment, but there is the potential for wrong values if content not ready
812- initialCSS = $scope.getInitialDimensions();
813-
814- // option to calculate the dimensions when layout is 'ready'
815- if (isStickyLayoutDeferred) {
816-
817- // logic: when this directive link() runs before the content has had a chance to layout on browser, height could be 0
818- if (!$elem[0].getBoundingClientRect().height) {
819-
820- onStickyHeighUnbind = $scope.$watch(
821- function() {
822- return $elem.height();
823- },
824-
825- // state change: sticky content's height set
826- function onStickyContentLayoutInitialHeightSet(newValue, oldValue) {
827- if (newValue > 0) {
828- // now can memorize
829- initialCSS = $scope.getInitialDimensions();
830-
831- if (!isStickyLayoutWatched) {
832- // preference was to do just a one-time async watch on the sticky's content; now stop watching
833- onStickyHeighUnbind();
834- }
835- }
836- }
837- );
838- }
839- }
840- }
841-
842- /**
843- * Determine if the element should be sticking or not.
844- */
845- var checkIfShouldStick = function() {
846- if ($scope.disabled === true || mediaQueryMatches()) {
847- if (isSticking) unStickElement();
848- return false;
849- }
850-
851- // What's the document client top for?
852- var scrollbarPosition = scrollbarYPos();
853- var shouldStick;
854-
855- if (anchor === 'top') {
856- if (confine === true) {
857- shouldStick = scrollbarPosition > stickyLine && scrollbarPosition <= stickyBottomLine;
858- } else {
859- shouldStick = scrollbarPosition > stickyLine;
860- }
861- } else {
862- shouldStick = scrollbarPosition <= stickyLine;
863- }
864-
865- // Switch the sticky mode if the element crosses the sticky line
866- // $attrs.stickLimit - when it's equal to true it enables the user
867- // to turn off the sticky function when the elem height is
868- // bigger then the viewport
869- var closestLine = getClosest (scrollbarPosition, stickyLine, stickyBottomLine);
870-
871- if (shouldStick && !shouldStickWithLimit ($attrs.stickLimit) && !isSticking) {
872- stickElement (closestLine);
873- } else if (!shouldStick && isSticking) {
874- unStickElement(closestLine, scrollbarPosition);
875- } else if (confine && !shouldStick) {
876- // If we are confined to the parent, refresh, and past the stickyBottomLine
877- // We should 'remember' the original offset and unstick the element which places it at the stickyBottomLine
878- originalOffset = elementsOffsetFromTop ($elem[0]);
879- unStickElement (closestLine, scrollbarPosition);
880- }
881- };
882-
883- /**
884- * determine the respective node that handles scrolling, defaulting to browser window
885- */
886- function deriveScrollingViewport(stickyNode) {
887- // derive relevant scrolling by ascending the DOM tree
888- var match =findAncestorTag (scrollableNodeTagName, stickyNode);
889- return (match.length === 1) ? match[0] : $window;
890- }
891-
892- /**
893- * since jqLite lacks closest(), this is a pseudo emulator (by tag name)
894- */
895- function findAncestorTag(tag, context) {
896- var m = []; // nodelist container
897- var n = context.parent(); // starting point
898- var p;
899-
900- do {
901- var node = n[0]; // break out of jqLite
902- // limit DOM territory
903- if (node.nodeType !== 1) {
904- break;
905- }
906-
907- // success
908- if (node.tagName.toUpperCase() === tag.toUpperCase()) {
909- return n;
910- }
911-
912- p = n.parent();
913- n = p; // set to parent
914- } while (p.length !== 0);
915-
916- return m; // empty set
917- }
918-
919- /**
920- * Seems to be undocumented functionality
921- */
922- function shouldStickWithLimit(shouldApplyWithLimit) {
923- return shouldApplyWithLimit === 'true'
924- ? ($window.innerHeight - ($elem[0].offsetHeight + parseInt(offset)) < 0)
925- : false;
926- }
927-
928- /**
929- * Finds the closest value from a set of numbers in an array.
930- */
931- function getClosest(scrollTop, stickyLine, stickyBottomLine) {
932- var closest = 'top';
933- var topDistance = Math.abs(scrollTop - stickyLine);
934- var bottomDistance = Math.abs(scrollTop - stickyBottomLine);
935-
936- if (topDistance > bottomDistance) {
937- closest = 'bottom';
938- }
939-
940- return closest;
941- }
942-
943- /**
944- * Unsticks the element
945- */
946- function unStickElement(fromDirection) {
947- if (initialStyle) {
948- $elem.attr('style', initialStyle);
949- }
950- isSticking = false;
951-
952- initialCSS.width = $scope.getInitialDimensions().width;
953-
954- $body.removeClass(bodyClass);
955- $elem.removeClass(stickyClass);
956- $elem.addClass(unstickyClass);
957-
958- if (fromDirection === 'top') {
959- $elem.removeClass(bottomClass);
960-
961- $elem
962- .css('z-index', 10)
963- .css('width', initialCSS.width)
964- .css('top', initialCSS.top)
965- .css('position', initialCSS.position)
966- .css('left', initialCSS.cssLeft)
967- .css('margin-top', initialCSS.marginTop)
968- .css('height', initialCSS.height);
969- } else if (fromDirection === 'bottom' && confine === true) {
970- $elem.addClass(bottomClass);
971-
972- // It's possible to page down page and skip the 'stickElement'.
973- // In that case we should create a placeholder so the offsets don't get off.
974- createPlaceholder();
975-
976- $elem
977- .css('z-index', 10)
978- .css('width', initialCSS.width)
979- .css('top', '')
980- .css('bottom', 0)
981- .css('position', 'absolute')
982- .css('left', initialCSS.cssLeft)
983- .css('margin-top', initialCSS.marginTop)
984- .css('margin-bottom', initialCSS.marginBottom)
985- .css('height', initialCSS.height);
986- }
987-
988- if (placeholder && fromDirection === anchor) {
989- placeholder.remove();
990- }
991- }
992-
993- /**
994- * Sticks the element
995- */
996- function stickElement(closestLine) {
997- // Set sticky state
998- isSticking = true;
999- $timeout(function() {
1000- initialCSS.offsetWidth = $elem[0].offsetWidth;
1001- }, 0);
1002- $body.addClass(bodyClass);
1003- $elem.removeClass(unstickyClass);
1004- $elem.removeClass(bottomClass);
1005- $elem.addClass(stickyClass);
1006-
1007- createPlaceholder();
1008-
1009- $elem
1010- .css('z-index', '10')
1011- .css('width', $elem[0].offsetWidth + 'px')
1012- .css('position', 'fixed')
1013- .css('left', $elem.css('left').replace('px', '') + 'px')
1014- .css(anchor, (offset + elementsOffsetFromTop (scrollbar)) + 'px')
1015- .css('margin-top', 0);
1016-
1017- if (anchor === 'bottom') {
1018- $elem.css('margin-bottom', 0);
1019- }
1020- }
1021-
1022- /**
1023- * Clean up directive
1024- */
1025- var onDestroy = function() {
1026- scrollbarElement.off('scroll', checkIfShouldStick);
1027- windowElement.off('resize', $onResize);
1028-
1029- $onResize = null;
1030-
1031- $body.removeClass(bodyClass);
1032-
1033- if (placeholder) {
1034- placeholder.remove();
1035- }
1036- };
1037-
1038- /**
1039- * Updates on resize.
1040- */
1041- function onResize() {
1042- unStickElement (anchor);
1043- checkIfShouldStick();
1044- }
1045-
1046- /**
1047- * Triggered on load / digest cycle
1048- * return `0` if the DOM element is hidden
1049- */
1050- var onDigest = function() {
1051- if ($scope.disabled === true) {
1052- return unStickElement();
1053- }
1054- var offsetFromTop = elementsOffsetFromTop ($elem[0]);
1055- if (offsetFromTop === 0) {
1056- return offsetFromTop;
1057- }
1058- if (anchor === 'top') {
1059- return (originalOffset || offsetFromTop) - elementsOffsetFromTop (scrollbar) + scrollbarYPos();
1060- } else {
1061- return offsetFromTop - scrollbarHeight() + $elem[0].offsetHeight + scrollbarYPos();
1062- }
1063- };
1064-
1065- /**
1066- * Triggered on change
1067- */
1068- var onChange = function (newVal, oldVal) {
1069-
1070- /**
1071- * Indicate if the DOM element is showed, or not
1072- * @type {boolean}
1073- */
1074- var elemIsShowed = !!newVal;
1075-
1076- /**
1077- * Indicate if the DOM element was showed, or not
1078- * @type {boolean}
1079- */
1080- var elemWasHidden = !oldVal;
1081- var valChange = (newVal !== oldVal || typeof stickyLine === 'undefined');
1082- var notSticking = (!isSticking && !isBottomedOut());
1083-
1084- if (valChange && notSticking && newVal > 0 && elemIsShowed) {
1085- stickyLine = newVal - offset;
1086- //Update dimensions of sticky element when is showed
1087- if (elemIsShowed && elemWasHidden) {
1088- $scope.updateStickyContentUpdateDimensions($elem[0].offsetWidth, $elem[0].offsetHeight);
1089- }
1090- // IF the sticky is confined, we want to make sure the parent is relatively positioned,
1091- // otherwise it won't bottom out properly
1092- if (confine) {
1093- $elem.parent().css({
1094- 'position': 'relative'
1095- });
1096- }
1097-
1098- // Get Parent height, so we know when to bottom out for confined stickies
1099- var parent = $elem.parent()[0];
1100-
1101- // Offset parent height by the elements height, if we're not using a placeholder
1102- var parentHeight = parseInt (parent.offsetHeight) - (usePlaceholder ? 0 : $elem[0].offsetHeight);
1103-
1104- // and now lets ensure we adhere to the bottom margins
1105- // TODO: make this an attribute? Maybe like ignore-margin?
1106- var marginBottom = parseInt ($elem.css('margin-bottom').replace(/px;?/, '')) || 0;
1107-
1108- // specify the bottom out line for the sticky to unstick
1109- var elementsDistanceFromTop = elementsOffsetFromTop ($elem[0]);
1110- var parentsDistanceFromTop = elementsOffsetFromTop (parent)
1111- var scrollbarDistanceFromTop = elementsOffsetFromTop (scrollbar);
1112-
1113- var elementsDistanceFromScrollbarStart = elementsDistanceFromTop - scrollbarDistanceFromTop;
1114- var elementsDistanceFromBottom = parentsDistanceFromTop + parentHeight - elementsDistanceFromTop;
1115-
1116- stickyBottomLine = elementsDistanceFromScrollbarStart
1117- + elementsDistanceFromBottom
1118- - $elem[0].offsetHeight
1119- - marginBottom
1120- - offset
1121- + +scrollbarYPos();
1122-
1123- checkIfShouldStick();
1124- }
1125- };
1126-
1127- /**
1128- * Helper Functions
1129- */
1130-
1131- /**
1132- * Create a placeholder
1133- */
1134- function createPlaceholder() {
1135- if (usePlaceholder) {
1136- // Remove the previous placeholder
1137- if (placeholder) {
1138- placeholder.remove();
1139- }
1140-
1141- placeholder = angular.element('<div>');
1142- var elementsHeight = $elem[0].offsetHeight;
1143- var computedStyle = $elem[0].currentStyle || window.getComputedStyle($elem[0]);
1144- elementsHeight += parseInt(computedStyle.marginTop, 10);
1145- elementsHeight += parseInt(computedStyle.marginBottom, 10);
1146- elementsHeight += parseInt(computedStyle.borderTopWidth, 10);
1147- elementsHeight += parseInt(computedStyle.borderBottomWidth, 10);
1148- placeholder.css('height', $elem[0].offsetHeight + 'px');
1149-
1150- $elem.after(placeholder);
1151- }
1152- }
1153-
1154- /**
1155- * Are we bottomed out of the parent element?
1156- */
1157- function isBottomedOut() {
1158- if (confine && scrollbarYPos() > stickyBottomLine) {
1159- return true;
1160- }
1161-
1162- return false;
1163- }
1164-
1165- /**
1166- * Fetch top offset of element
1167- */
1168- function elementsOffsetFromTop(element) {
1169- var offset = 0;
1170-
1171- if (element.getBoundingClientRect) {
1172- offset = element.getBoundingClientRect().top;
1173- }
1174-
1175- return offset;
1176- }
1177-
1178- /**
1179- * Retrieves top scroll distance
1180- */
1181- function scrollbarYPos() {
1182- var position;
1183-
1184- if (typeof scrollbar.scrollTop !== 'undefined') {
1185- position = scrollbar.scrollTop;
1186- } else if (typeof scrollbar.pageYOffset !== 'undefined') {
1187- position = scrollbar.pageYOffset;
1188- } else {
1189- position = document.documentElement.scrollTop;
1190- }
1191-
1192- return position;
1193- }
1194-
1195- /**
1196- * Determine scrollbar's height
1197- */
1198- function scrollbarHeight() {
1199- var height;
1200-
1201- if (scrollbarElement[0] instanceof HTMLElement) {
1202- // isn't bounding client rect cleaner than insane regex mess?
1203- height = $window.getComputedStyle(scrollbarElement[0], null)
1204- .getPropertyValue('height')
1205- .replace(/px;?/, '');
1206- } else {
1207- height = $window.innerHeight;
1208- }
1209-
1210- return parseInt (height) || 0;
1211- }
1212-
1213- /**
1214- * Checks if the media matches
1215- */
1216- function mediaQueryMatches() {
1217- var mediaQuery = $attrs.mediaQuery || false;
1218- var matchMedia = $window.matchMedia;
1219-
1220- return mediaQuery && !(matchMedia ('(' + mediaQuery + ')').matches || matchMedia (mediaQuery).matches);
1221- }
1222-
1223- /**
1224- * Get more accurate CSS values
1225- */
1226- function getCSS($el, prop){
1227- var el = $el[0],
1228- computed = window.getComputedStyle(el),
1229- prevDisplay = computed.display,
1230- val;
1231-
1232- // hide the element so that we can get original css
1233- // values instead of computed values
1234- el.style.display = "none";
1235-
1236- // NOTE - computed style declaration object is a reference
1237- // to the element's CSSStyleDeclaration, so it will always
1238- // reflect the current style of the element
1239- val = computed[prop];
1240-
1241- // restore previous display value
1242- el.style.display = prevDisplay;
1243-
1244- return val;
1245- }
1246-
1247- // public accessors for the controller to hitch into. Helps with external API access
1248- $scope.getElement = function() { return $elem; };
1249- $scope.getScrollbar = function() { return scrollbar; };
1250- $scope.getInitialCSS = function() { return initialCSS; };
1251- $scope.getAnchor = function() { return anchor; };
1252- $scope.isSticking = function() { return isSticking; };
1253- $scope.getOriginalInitialCSS = function() { return originalInitialCSS; };
1254- // pass through aliases
1255- $scope.processUnStickElement = function(anchor) { unStickElement(anchor)};
1256- $scope.processCheckIfShouldStick =function() { checkIfShouldStick(); };
1257-
1258- /**
1259- * set the dimensions for the defaults of the content block occupied by the sticky element
1260- */
1261- $scope.getInitialDimensions = function() {
1262- return {
1263- zIndex: $elem.css('z-index'),
1264- top: $elem.css('top'),
1265- position: initialPosition, // revert to true initial state
1266- marginTop: $elem.css('margin-top'),
1267- marginBottom: $elem.css('margin-bottom'),
1268- cssLeft: getCSS($elem, 'left'),
1269- width: $elem[0].offsetWidth,
1270- height: $elem.css('height')
1271- };
1272- };
1273-
1274- /**
1275- * only change content box dimensions
1276- */
1277- $scope.updateStickyContentUpdateDimensions = function(width, height) {
1278- if (width && height) {
1279- initSticky();
1280- initialCSS.width = width + 'px';
1281- initialCSS.height = height + 'px';
1282- }
1283- };
1284-
1285- // ----------- configuration -----------
1286-
1287- $timeout(function() {
1288- originalInitialCSS = $scope.getInitialDimensions(); // preserve a copy
1289- // Init the directive
1290- initSticky();
1291- },0);
1292- },
1293-
1294- /**
1295- * +++++++++ public APIs+++++++++++++
1296- */
1297- controller: ['$scope', '$window', function($scope, $window) {
1298-
1299- /**
1300- * integration method allows for an outside client to reset the pinned state back to unpinned.
1301- * Useful for when refreshing the scrollable DIV content completely
1302- * if newWidth and newHeight integer values are not supplied then function will make a best guess
1303- */
1304- this.resetLayout = function(newWidth, newHeight) {
1305-
1306- var scrollbar = $scope.getScrollbar(),
1307- initialCSS = $scope.getInitialCSS(),
1308- anchor = $scope.getAnchor();
1309-
1310- function _resetScrollPosition() {
1311-
1312- // reset means content is scrolled to anchor position
1313- if (anchor === 'top') {
1314- // window based scroller
1315- if (scrollbar === $window) {
1316- $window.scrollTo(0, 0);
1317- // DIV based sticky scroller
1318- } else {
1319- if (scrollbar.scrollTop > 0) {
1320- scrollbar.scrollTop = 0;
1321- }
1322- }
1323- }
1324- // todo: need bottom use case
1325- }
1326-
1327- // only if pinned, force unpinning, otherwise height is inadvertently reset to 0
1328- if ($scope.isSticking()) {
1329- $scope.processUnStickElement (anchor);
1330- $scope.processCheckIfShouldStick();
1331- }
1332- // remove layout-affecting attribures that were modified by this sticky
1333- $scope.getElement().css({ 'width': '', 'height': '', 'position': '', 'top': '', zIndex: '' });
1334- // model resets
1335- initialCSS.position = $scope.getOriginalInitialCSS().position; // revert to original state
1336- delete initialCSS.offsetWidth; // stickElement affected
1337-
1338- // use this directive element's as default, if no measurements passed in
1339- if (newWidth === undefined && newHeight === undefined) {
1340- var e_bcr = $scope.getElement()[0].getBoundingClientRect();
1341- newWidth = e_bcr.width;
1342- newHeight = e_bcr.height;
1343- }
1344-
1345- // update model with new dimensions (if supplied from client's own measurement)
1346- $scope.updateStickyContentUpdateDimensions(newWidth, newHeight); // update layout dimensions only
1347-
1348- _resetScrollPosition();
1349- };
1350-
1351- /**
1352- * return a reference to the scrolling element (window or DIV with overflow)
1353- */
1354- this.getScrollbar = function() {
1355- return $scope.getScrollbar();
1356- };
1357- }]
1358- };
1359- }]
1360- );
1361-
1362- // Shiv: matchMedia
1363- window.matchMedia = window.matchMedia || (function() {
1364- var warning = 'angular-sticky: This browser does not support ' +
1365- 'matchMedia, therefore the minWidth option will not work on ' +
1366- 'this browser. Polyfill matchMedia to fix this issue.';
1367-
1368- if (window.console && console.warn) {
1369- console.warn(warning);
1370- }
1371-
1372- return function() {
1373- return {
1374- matches: true
1375- };
1376- };
1377- }());
1378-}());
1379diff --git a/src/maasserver/static/js/angular/maas.js b/src/maasserver/static/js/angular/maas.js
1380index 0456a2f..cda7a27 100755
1381--- a/src/maasserver/static/js/angular/maas.js
1382+++ b/src/maasserver/static/js/angular/maas.js
1383@@ -9,8 +9,7 @@
1384 */
1385
1386 angular.module('MAAS',
1387- ['ngRoute', 'ngCookies', 'ngSanitize', 'ngTagsInput', 'sticky',
1388- 'vs-repeat']).config(
1389+ ['ngRoute', 'ngCookies', 'ngSanitize', 'ngTagsInput', 'vs-repeat']).config(
1390 function($interpolateProvider, $routeProvider, $httpProvider) {
1391 $interpolateProvider.startSymbol('{$');
1392 $interpolateProvider.endSymbol('$}');
1393diff --git a/src/maasserver/static/partials/node-details.html b/src/maasserver/static/partials/node-details.html
1394index a217103..77c760c 100755
1395--- a/src/maasserver/static/partials/node-details.html
1396+++ b/src/maasserver/static/partials/node-details.html
1397@@ -6,7 +6,7 @@
1398 </header>
1399 </div>
1400 <div data-ng-if="loaded">
1401- <header sticky use-placeholder="true" media-query="min-width: 769px" class="p-strip--light is-shallow page-header u-no-padding--bottom">
1402+ <header class="p-strip--light is-shallow page-header u-no-padding--bottom">
1403 <div class="row">
1404 <div class="col-8">
1405 <h1 class="page-header__title">
1406diff --git a/src/maasserver/static/partials/node-events.html b/src/maasserver/static/partials/node-events.html
1407index 59e3e91..d9a5c5e 100644
1408--- a/src/maasserver/static/partials/node-events.html
1409+++ b/src/maasserver/static/partials/node-events.html
1410@@ -6,7 +6,7 @@
1411 </header>
1412 </div>
1413 <div class="ng-hide u-no-margin--top" data-ng-show="loaded">
1414- <header class="p-strip--light is-shallow is-bordered page-header" sticky>
1415+ <header class="p-strip--light is-shallow is-bordered page-header">
1416 <div class="row">
1417 <div class="col-8">
1418 <ul class="p-inline-list u-no-margin--top">

Subscribers

People subscribed via source and target branches