diff --git a/src/popover/popover.js b/src/popover/popover.js index 2bea0a3e10..2d7ed97b0c 100644 --- a/src/popover/popover.js +++ b/src/popover/popover.js @@ -9,7 +9,6 @@ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] ) return { restrict: 'EA', replace: true, - scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' }, templateUrl: 'template/popover/popover.html' }; }) diff --git a/src/popover/test/popover.spec.js b/src/popover/test/popover.spec.js index 5f1fdc7f05..72e3cadae0 100644 --- a/src/popover/test/popover.spec.js +++ b/src/popover/test/popover.spec.js @@ -19,11 +19,11 @@ describe('popover', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('span'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); })); it('should not be open initially', inject(function() { - expect( elmScope.tt_isOpen ).toBe( false ); + expect( elmScope.isOpen ).toBe( false ); // We can only test *that* the popover-popup element wasn't created as the // implementation is templated and replaced. @@ -32,7 +32,7 @@ describe('popover', function() { it('should open on click', inject(function() { elm.trigger( 'click' ); - expect( elmScope.tt_isOpen ).toBe( true ); + expect( elmScope.isOpen ).toBe( true ); // We can only test *that* the popover-popup element was created as the // implementation is templated and replaced. @@ -42,7 +42,7 @@ describe('popover', function() { it('should close on second click', inject(function() { elm.trigger( 'click' ); elm.trigger( 'click' ); - expect( elmScope.tt_isOpen ).toBe( false ); + expect( elmScope.isOpen ).toBe( false ); })); it('should not unbind event handlers created by other directives - issue 456', inject( function( $compile ) { diff --git a/src/tooltip/test/tooltip.spec.js b/src/tooltip/test/tooltip.spec.js index 98f51bcfef..a9838b2d50 100644 --- a/src/tooltip/test/tooltip.spec.js +++ b/src/tooltip/test/tooltip.spec.js @@ -19,11 +19,11 @@ describe('tooltip', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('span'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); })); it('should not be open initially', inject(function() { - expect( elmScope.tt_isOpen ).toBe( false ); + expect( elmScope.isOpen ).toBe( false ); // We can only test *that* the tooltip-popup element wasn't created as the // implementation is templated and replaced. @@ -32,7 +32,7 @@ describe('tooltip', function() { it('should open on mouseenter', inject(function() { elm.trigger( 'mouseenter' ); - expect( elmScope.tt_isOpen ).toBe( true ); + expect( elmScope.isOpen ).toBe( true ); // We can only test *that* the tooltip-popup element was created as the // implementation is templated and replaced. @@ -42,16 +42,16 @@ describe('tooltip', function() { it('should close on mouseleave', inject(function() { elm.trigger( 'mouseenter' ); elm.trigger( 'mouseleave' ); - expect( elmScope.tt_isOpen ).toBe( false ); + expect( elmScope.isOpen ).toBe( false ); })); it('should not animate on animation set to false', inject(function() { - expect( elmScope.tt_animation ).toBe( false ); + expect( elmScope.animation ).toBe( false ); })); it('should have default placement of "top"', inject(function() { elm.trigger( 'mouseenter' ); - expect( elmScope.tt_placement ).toBe( "top" ); + expect( elmScope.placement ).toBe( "top" ); })); it('should allow specification of placement', inject( function( $compile ) { @@ -59,10 +59,10 @@ describe('tooltip', function() { 'Selector Text' ) )( scope ); scope.$apply(); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); elm.trigger( 'mouseenter' ); - expect( elmScope.tt_placement ).toBe( "bottom" ); + expect( elmScope.placement ).toBe( "bottom" ); })); it('should work inside an ngRepeat', inject( function( $compile ) { @@ -86,14 +86,12 @@ describe('tooltip', function() { tt.trigger( 'mouseenter' ); expect( tt.text() ).toBe( scope.items[0].name ); - expect( tt.scope().tt_content ).toBe( scope.items[0].tooltip ); + expect( tt.isolateScope().content ).toBe( scope.items[0].tooltip ); tt.trigger( 'mouseleave' ); })); it('should only have an isolate scope on the popup', inject( function ( $compile ) { - var ttScope; - scope.tooltipMsg = "Tooltip Text"; scope.alt = "Alt Message"; @@ -104,23 +102,21 @@ describe('tooltip', function() { $compile( elmBody )( scope ); scope.$digest(); elm = elmBody.find( 'span' ); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); elm.trigger( 'mouseenter' ); expect( elm.attr( 'alt' ) ).toBe( scope.alt ); - ttScope = angular.element( elmBody.children()[1] ).isolateScope(); - expect( ttScope.placement ).toBe( 'top' ); - expect( ttScope.content ).toBe( scope.tooltipMsg ); + expect( elmScope.placement ).toBe( 'top' ); + expect( elmScope.content ).toBe( scope.tooltipMsg ); elm.trigger( 'mouseleave' ); //Isolate scope contents should be the same after hiding and showing again (issue 1191) elm.trigger( 'mouseenter' ); - ttScope = angular.element( elmBody.children()[1] ).isolateScope(); - expect( ttScope.placement ).toBe( 'top' ); - expect( ttScope.content ).toBe( scope.tooltipMsg ); + expect( elmScope.placement ).toBe( 'top' ); + expect( elmScope.content ).toBe( scope.tooltipMsg ); })); it('should not show tooltips if there is nothing to show - issue #129', inject(function ($compile) { @@ -136,30 +132,14 @@ describe('tooltip', function() { it( 'should close the tooltip when its trigger element is destroyed', inject( function() { elm.trigger( 'mouseenter' ); - expect( elmScope.tt_isOpen ).toBe( true ); + expect( elmScope.isOpen ).toBe( true ); elm.remove(); elmScope.$destroy(); expect( elmBody.children().length ).toBe( 0 ); })); - it('issue 1191 - isolate scope on the popup should always be child of correct element scope', inject( function ( $compile ) { - var ttScope; - elm.trigger( 'mouseenter' ); - - ttScope = angular.element( elmBody.children()[1] ).isolateScope(); - expect( ttScope.$parent ).toBe( elmScope ); - - elm.trigger( 'mouseleave' ); - - // After leaving and coming back, the scope's parent should be the same - elm.trigger( 'mouseenter' ); - - ttScope = angular.element( elmBody.children()[1] ).isolateScope(); - expect( ttScope.$parent ).toBe( elmScope ); - - elm.trigger( 'mouseleave' ); - })); + // issue 1191 no longer valid describe('with specified enable expression', function() { @@ -170,14 +150,14 @@ describe('tooltip', function() { ))(scope); scope.$digest(); elm = elmBody.find('span'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); })); it('should not open ', inject(function () { elm.trigger('mouseenter'); - expect(elmScope.tt_isOpen).toBeFalsy(); + expect(elmScope.isOpen).toBeFalsy(); expect(elmBody.children().length).toBe(1); })); @@ -187,7 +167,7 @@ describe('tooltip', function() { scope.enable = true; scope.$digest(); elm.trigger('mouseenter'); - expect(elmScope.tt_isOpen).toBeTruthy(); + expect(elmScope.isOpen).toBeTruthy(); expect(elmBody.children().length).toBe(2); })); @@ -200,34 +180,34 @@ describe('tooltip', function() { elm = $compile(angular.element( 'Selector Text' ))(scope); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); scope.$digest(); })); it('should open after timeout', inject(function ($timeout) { elm.trigger('mouseenter'); - expect(elmScope.tt_isOpen).toBe(false); + expect(elmScope.isOpen).toBe(false); $timeout.flush(); - expect(elmScope.tt_isOpen).toBe(true); + expect(elmScope.isOpen).toBe(true); })); it('should not open if mouseleave before timeout', inject(function ($timeout) { elm.trigger('mouseenter'); - expect(elmScope.tt_isOpen).toBe(false); + expect(elmScope.isOpen).toBe(false); elm.trigger('mouseleave'); $timeout.flush(); - expect(elmScope.tt_isOpen).toBe(false); + expect(elmScope.isOpen).toBe(false); })); it('should use default popup delay if specified delay is not a number', function(){ scope.delay='text1000'; scope.$digest(); elm.trigger('mouseenter'); - expect(elmScope.tt_isOpen).toBe(true); + expect(elmScope.isOpen).toBe(true); }); }); @@ -246,13 +226,13 @@ describe('tooltip', function() { $compile(elmBody)(scope); scope.$apply(); elm = elmBody.find('input'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); elm.trigger('focus'); - expect( elmScope.tt_isOpen ).toBeTruthy(); + expect( elmScope.isOpen ).toBeTruthy(); elm.trigger('blur'); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); })); it( 'should use it as both the show and hide triggers for unmapped triggers', inject( function( $compile ) { @@ -262,13 +242,13 @@ describe('tooltip', function() { $compile(elmBody)(scope); scope.$apply(); elm = elmBody.find('input'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); elm.trigger('fakeTriggerAttr'); - expect( elmScope.tt_isOpen ).toBeTruthy(); + expect( elmScope.isOpen ).toBeTruthy(); elm.trigger('fakeTriggerAttr'); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); })); it('should not share triggers among different element instances - issue 692', inject( function ($compile) { @@ -285,16 +265,16 @@ describe('tooltip', function() { scope.$apply(); var elm1 = elmBody.find('input').eq(0); var elm2 = elmBody.find('input').eq(1); - var elmScope1 = elm1.scope(); - var elmScope2 = elm2.scope(); + var elmScope1 = elm1.isolateScope(); + var elmScope2 = elm2.isolateScope(); scope.$apply('test = false'); elm2.trigger('mouseenter'); - expect( elmScope2.tt_isOpen ).toBeFalsy(); + expect( elmScope2.isOpen ).toBeFalsy(); elm2.click(); - expect( elmScope2.tt_isOpen ).toBeTruthy(); + expect( elmScope2.isOpen ).toBeTruthy(); })); }); @@ -314,12 +294,12 @@ describe('tooltip', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('span'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); var bodyLength = $body.children().length; elm.trigger( 'mouseenter' ); - expect( elmScope.tt_isOpen ).toBe( true ); + expect( elmScope.isOpen ).toBe( true ); expect( elmBody.children().length ).toBe( 1 ); expect( $body.children().length ).toEqual( bodyLength + 1 ); })); @@ -332,7 +312,8 @@ describe('tooltip', function() { var match = false; angular.forEach(angular.element.cache, function (item) { - if (item.data && item.data.$isolateScope === tooltipScope) { + // tooltip popup's scope is a non-isolate child scope of tooltip directive + if (item.data && item.data.$scope === tooltipScope) { match = true; } }); @@ -347,7 +328,7 @@ describe('tooltip', function() { $rootScope.$apply(); elm = elmBody.find('input'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); elm.trigger('fooTrigger'); tooltipScope = elmScope.$$childTail; })); @@ -414,7 +395,7 @@ describe( 'tooltipHtmlUnsafe', function() { ))( scope ); scope.$digest(); elm = elmBody.find('span'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); })); it( 'should render html properly', inject( function () { @@ -423,16 +404,16 @@ describe( 'tooltipHtmlUnsafe', function() { })); it( 'should show on mouseenter and hide on mouseleave', inject( function () { - expect( elmScope.tt_isOpen ).toBe( false ); + expect( elmScope.isOpen ).toBe( false ); elm.trigger( 'mouseenter' ); - expect( elmScope.tt_isOpen ).toBe( true ); + expect( elmScope.isOpen ).toBe( true ); expect( elmBody.children().length ).toBe( 2 ); - expect( elmScope.tt_content ).toEqual( scope.html ); + expect( elmScope.content ).toEqual( scope.html ); elm.trigger( 'mouseleave' ); - expect( elmScope.tt_isOpen ).toBe( false ); + expect( elmScope.isOpen ).toBe( false ); expect( elmBody.children().length ).toBe( 1 ); })); }); @@ -461,16 +442,16 @@ describe( '$tooltipProvider', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('span'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); })); it('should open after timeout', inject(function($timeout) { elm.trigger( 'mouseenter' ); - expect( elmScope.tt_isOpen ).toBe( false ); + expect( elmScope.isOpen ).toBe( false ); $timeout.flush(); - expect( elmScope.tt_isOpen ).toBe( true ); + expect( elmScope.isOpen ).toBe( true ); })); @@ -495,12 +476,12 @@ describe( '$tooltipProvider', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('span'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); var bodyLength = $body.children().length; elm.trigger( 'mouseenter' ); - expect( elmScope.tt_isOpen ).toBe( true ); + expect( elmScope.isOpen ).toBe( true ); expect( elmBody.children().length ).toBe( 1 ); expect( $body.children().length ).toEqual( bodyLength + 1 ); })); @@ -515,14 +496,14 @@ describe( '$tooltipProvider', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('span'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); elm.trigger( 'mouseenter' ); - expect( elmScope.tt_isOpen ).toBe( true ); + expect( elmScope.isOpen ).toBe( true ); scope.$broadcast('$locationChangeSuccess'); scope.$digest(); - expect( elmScope.tt_isOpen ).toBe( false ); + expect( elmScope.isOpen ).toBe( false ); })); }); @@ -544,13 +525,13 @@ describe( '$tooltipProvider', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('input'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); elm.trigger('focus'); - expect( elmScope.tt_isOpen ).toBeTruthy(); + expect( elmScope.isOpen ).toBeTruthy(); elm.trigger('blur'); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); })); it( 'should override the show and hide triggers if there is an attribute', inject( function ( $rootScope, $compile ) { @@ -562,13 +543,13 @@ describe( '$tooltipProvider', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('input'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); elm.trigger('mouseenter'); - expect( elmScope.tt_isOpen ).toBeTruthy(); + expect( elmScope.isOpen ).toBeTruthy(); elm.trigger('mouseleave'); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); })); }); @@ -590,13 +571,13 @@ describe( '$tooltipProvider', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('input'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); elm.trigger('customOpenTrigger'); - expect( elmScope.tt_isOpen ).toBeTruthy(); + expect( elmScope.isOpen ).toBeTruthy(); elm.trigger('customCloseTrigger'); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); })); }); @@ -617,13 +598,13 @@ describe( '$tooltipProvider', function() { $compile(elmBody)(scope); scope.$digest(); elm = elmBody.find('span'); - elmScope = elm.scope(); + elmScope = elm.isolateScope(); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); elm.trigger('fakeTrigger'); - expect( elmScope.tt_isOpen ).toBeTruthy(); + expect( elmScope.isOpen ).toBeTruthy(); elm.trigger('fakeTrigger'); - expect( elmScope.tt_isOpen ).toBeFalsy(); + expect( elmScope.isOpen ).toBeFalsy(); })); }); }); diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js index c6a610f8f3..e8fc58c2e8 100644 --- a/src/tooltip/tooltip.js +++ b/src/tooltip/tooltip.js @@ -93,21 +93,17 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap var directiveName = snake_case( type ); - var startSym = $interpolate.startSymbol(); - var endSym = $interpolate.endSymbol(); - var template = - '
'+ '
'; return { restrict: 'EA', - scope: true, + scope: { + title: '@' + prefix + 'Title' + }, compile: function (tElem, tAttrs) { var tooltipLinker = $compile( template ); @@ -134,7 +130,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap // Calculate the tooltip's top and left coordinates to center it with // this directive. - switch ( scope.tt_placement ) { + switch ( scope.placement ) { case 'right': ttPosition = { top: position.top + position.height / 2 - ttHeight / 2, @@ -171,10 +167,10 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap // By default, the tooltip is not open. // TODO add ability to start tooltip opened - scope.tt_isOpen = false; + scope.isOpen = false; function toggleTooltipBind () { - if ( ! scope.tt_isOpen ) { + if ( ! scope.isOpen ) { showTooltipBind(); } else { hideTooltipBind(); @@ -183,7 +179,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap // Show the tooltip with delay if specified, otherwise show it immediately function showTooltipBind() { - if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) { + if(hasEnableExp && !scope.$parent.$eval(attrs[prefix+'Enable'])) { return; } if ( scope.tt_popupDelay ) { @@ -205,7 +201,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap // Don't show empty tooltips. - if ( ! scope.tt_content ) { + if ( ! scope.content ) { return angular.noop; } @@ -231,7 +227,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap positionTooltip(); // And show the tooltip. - scope.tt_isOpen = true; + scope.isOpen = true; scope.$digest(); // digest required as $apply is not called // Return positioning function as promise callback for correct @@ -242,7 +238,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap // Hide the tooltip popup element. function hide() { // First things first: we don't show it anymore. - scope.tt_isOpen = false; + scope.isOpen = false; //if tooltip is going to be shown after delay, we must cancel this $timeout.cancel( popupTimeout ); @@ -250,7 +246,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap // And now we remove it from the DOM. However, if we have animation, we // need to wait for it to expire beforehand. // FIXME: this is a placeholder for a port of the transitions library. - if ( scope.tt_animation ) { + if ( scope.animation ) { transitionTimeout = $timeout(removeTooltip, 500); } else { removeTooltip(); @@ -262,7 +258,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap if (tooltip) { removeTooltip(); } - tooltip = tooltipLinker(scope, function () {}); + // Make sure to use a new child scope every time as watchers leak into scope. + // If linked DOM is removed, watchers from that DOM isn't removed. + tooltip = tooltipLinker(scope.$new(), function () {}); // Get contents rendered into the tooltip scope.$digest(); @@ -279,19 +277,15 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap * Observe the relevant attributes. */ attrs.$observe( type, function ( val ) { - scope.tt_content = val; + scope.content = val; - if (!val && scope.tt_isOpen ) { + if (!val && scope.isOpen ) { hide(); } }); - attrs.$observe( prefix+'Title', function ( val ) { - scope.tt_title = val; - }); - attrs.$observe( prefix+'Placement', function ( val ) { - scope.tt_placement = angular.isDefined( val ) ? val : options.placement; + scope.placement = angular.isDefined( val ) ? val : options.placement; }); attrs.$observe( prefix+'PopupDelay', function ( val ) { @@ -322,7 +316,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap }); var animation = scope.$eval(attrs[prefix + 'Animation']); - scope.tt_animation = angular.isDefined(animation) ? !!animation : options.animation; + scope.animation = angular.isDefined(animation) ? !!animation : options.animation; attrs.$observe( prefix+'AppendToBody', function ( val ) { appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody; @@ -333,7 +327,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap // by the change. if ( appendToBody ) { scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () { - if ( scope.tt_isOpen ) { + if ( scope.isOpen ) { hide(); } }); @@ -357,7 +351,6 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap return { restrict: 'EA', replace: true, - scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, templateUrl: 'template/tooltip/tooltip-popup.html' }; }) @@ -370,7 +363,6 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap return { restrict: 'EA', replace: true, - scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html' }; }) diff --git a/template/popover/popover.html b/template/popover/popover.html index 5929ee6e6a..487cbe48f7 100644 --- a/template/popover/popover.html +++ b/template/popover/popover.html @@ -1,4 +1,4 @@ -
+
diff --git a/template/tooltip/tooltip-html-unsafe-popup.html b/template/tooltip/tooltip-html-unsafe-popup.html index 129016d9c1..78addf077b 100644 --- a/template/tooltip/tooltip-html-unsafe-popup.html +++ b/template/tooltip/tooltip-html-unsafe-popup.html @@ -1,4 +1,4 @@ -
+
diff --git a/template/tooltip/tooltip-popup.html b/template/tooltip/tooltip-popup.html index fd51120774..493d2bf896 100644 --- a/template/tooltip/tooltip-popup.html +++ b/template/tooltip/tooltip-popup.html @@ -1,4 +1,4 @@ -
+