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