Wikipedia/WRTKit/UI/ContentPanel.js
changeset 20 918767a9c8d3
equal deleted inserted replaced
19:f3521a11d878 20:918767a9c8d3
       
     1 /*
       
     2 � Copyright 2008 Nokia Corporation. All rights reserved.
       
     3 
       
     4 IMPORTANT:  The Nokia software ("WRTKit and Example Widget files") is supplied to you by Nokia
       
     5 Corporation (�Nokia�) in consideration of your agreement to the following terms. Your use, installation
       
     6 and/or redistribution of the WRTKit and Example Widget files constitutes acceptance of these terms. If
       
     7 you do not agree with these terms, please do not use, install, or redistribute the WRTKit and Example
       
     8 Widget files.
       
     9 
       
    10 In consideration of your agreement to abide by the following terms, and subject to these terms, Nokia
       
    11 grants you a personal, non-exclusive license, under Nokia�s copyrights in the WRTKit and Example
       
    12 Widget files, to use, reproduce, and redistribute the WRTKit and Example files, in text form (for HTML,
       
    13 CSS, or JavaScript files) or binary form (for associated images), for the sole purpose of creating S60
       
    14 Widgets.
       
    15 
       
    16 If you redistribute the WRTKit and Example files, you must retain this entire notice in all such
       
    17 redistributions of the WRTKit and Example files.
       
    18 
       
    19 You may not use the name, trademarks, service marks or logos of Nokia to endorse or promote products
       
    20 that include the WRTKit and Example files without the prior written explicit agreement with Nokia.
       
    21 Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by
       
    22 Nokia herein, including but not limited to any patent rights that may be infringed by your products that
       
    23 incorporate the WRTKit and Example files or by other works in which the WRTKit and Example files
       
    24 may be incorporated.
       
    25 
       
    26 The WRTKit and Example files are provided on an "AS IS" basis.  NOKIA MAKES NO
       
    27 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
       
    28 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
       
    29 PARTICULAR PURPOSE, REGARDING THE EXAMPLES OR ITS USE AND OPERATION
       
    30 ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
       
    31 
       
    32 IN NO EVENT SHALL NOKIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
       
    33 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
       
    34 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
       
    35 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, AND/OR
       
    36 DISTRIBUTION OF THE EXAMPLES, HOWEVER CAUSED AND WHETHER UNDER THEORY
       
    37 OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE,
       
    38 EVEN IF NOKIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    39 
       
    40 */
       
    41 
       
    42 ///////////////////////////////////////////////////////////////////////////////
       
    43 // The ContentPanel class is a control for displaying content. The panel
       
    44 // can be expanded and collapsed.
       
    45 
       
    46 // Constructor.
       
    47 function ContentPanel(id, caption, content, foldable, expanded) {
       
    48     if (id != UI_NO_INIT_ID) {
       
    49         this.init(id, caption, content, foldable, expanded);
       
    50     }
       
    51 }
       
    52 
       
    53 // ContentPanel inherits from Control.
       
    54 ContentPanel.prototype = new Control(UI_NO_INIT_ID);
       
    55 
       
    56 // The element hierarchy in a content panel is as follows:
       
    57 //
       
    58 // rootElement
       
    59 //     assemblyElement
       
    60 //         captionElement
       
    61 //             foldToggleElement
       
    62 //                 captionLinkElement
       
    63 //                     captionTextElement
       
    64 //     contentElement
       
    65 //
       
    66 // captionTextElement is moved under foldToggleElement if disabled
       
    67 // or captionElement if not foldable
       
    68 
       
    69 // The fold toggle element used for folding content panels.
       
    70 ContentPanel.prototype.foldToggleElement = null;
       
    71 
       
    72 // The caption link element of this control.
       
    73 ContentPanel.prototype.captionLinkElement = null;
       
    74 
       
    75 // The caption text element of this control.
       
    76 ContentPanel.prototype.captionTextElement = null;
       
    77 
       
    78 // The content element of this control.
       
    79 ContentPanel.prototype.contentElement = null;
       
    80 
       
    81 // The foldable state of this control.
       
    82 ContentPanel.prototype.foldable = false;
       
    83 
       
    84 // The expanded state of this control.
       
    85 ContentPanel.prototype.expanded = false;
       
    86 
       
    87 // Enabled status.
       
    88 ContentPanel.prototype.enabled = false;
       
    89 
       
    90 // Initializer - called from constructor.
       
    91 ContentPanel.prototype.init = function(id, caption, content, foldable, expanded) {
       
    92     uiLogger.debug("ContentPanel.init(" + id + ", " + caption + ", " + content + ", " + foldable + ", " + expanded + ")");
       
    93     
       
    94     // call superclass initializer
       
    95     Control.prototype.init.call(this, id, caption);
       
    96     
       
    97     // the control defaults to enabled
       
    98     this.enabled = true;
       
    99     
       
   100     // create caption text element
       
   101     this.captionTextElement = document.createElement("span");
       
   102     
       
   103     // disconnect the control element
       
   104     this.assemblyElement.removeChild(this.controlElement);
       
   105     
       
   106     // set the foldable state
       
   107     this.foldable = foldable;
       
   108     
       
   109     var self = this;
       
   110     // is this a foldable content panel?
       
   111     if (foldable) {
       
   112         // create fold toggle element
       
   113         this.foldToggleElement = document.createElement("div");
       
   114         this.captionElement.appendChild(this.foldToggleElement);
       
   115         
       
   116         // create caption link and add to caption element
       
   117         this.captionLinkElement = document.createElement("a");
       
   118         this.captionLinkElement.href = "JavaScript:void(0)";
       
   119         this.foldToggleElement.appendChild(this.captionLinkElement);
       
   120         
       
   121         // add the text element to the link element
       
   122         this.captionLinkElement.appendChild(this.captionTextElement);
       
   123         
       
   124         // bind event listeners
       
   125         this.captionLinkElement.addEventListener("focus", function() { self.focusStateChanged(true); }, false);
       
   126         this.captionLinkElement.addEventListener("blur", function() { self.focusStateChanged(false); }, false);
       
   127         this.foldToggleElement.addEventListener("mouseover", function() { self.hoverStateChanged(true); }, false);
       
   128         this.foldToggleElement.addEventListener("mouseout", function() { self.hoverStateChanged(false); }, false);
       
   129         this.foldToggleElement.addEventListener("mousedown", function(event) {
       
   130                                                                  self.captionClicked();
       
   131                                                                  event.stopPropagation();
       
   132                                                                  event.preventDefault();
       
   133                                                              }, true);
       
   134         this.foldToggleElement.addEventListener("keydown", function(event) {
       
   135                                                                // center and enter trigger the action
       
   136                                                                if (event.keyCode == 0 || event.keyCode == 13) {
       
   137                                                                    self.captionClicked();
       
   138                                                                    event.stopPropagation();
       
   139                                                                    event.preventDefault();
       
   140                                                                }
       
   141                                                            }, true);
       
   142         this.expanded = expanded;
       
   143     } else {
       
   144         // since this is not a foldable panel the content should be expanded
       
   145         this.expanded = true;
       
   146         
       
   147         // add the text element directly to the caption element
       
   148         this.captionElement.appendChild(this.captionTextElement);
       
   149     }
       
   150     
       
   151     // create content element
       
   152     this.contentElement = document.createElement("div");
       
   153     this.contentElement.style.display = this.expanded ? "block" : "none";
       
   154     this.rootElement.appendChild(this.contentElement);
       
   155 	
       
   156 	if (foldable) {
       
   157 		this.contentElement.onclick = function(event){
       
   158 			self.captionClicked();
       
   159 		};
       
   160 	}
       
   161     // set caption, content and expanded state
       
   162     this.setCaption(caption);
       
   163     this.setContent(content);
       
   164     
       
   165     // update style
       
   166     this.updateStyleFromState();
       
   167 }
       
   168 
       
   169 // Returns the enabled state.
       
   170 ContentPanel.prototype.isEnabled = function() {
       
   171     return this.enabled;
       
   172 }
       
   173 
       
   174 // Sets the enabled state.
       
   175 ContentPanel.prototype.setEnabled = function(enabled) {
       
   176     uiLogger.debug("ContentPanel.setEnabled(" + enabled + ")");
       
   177     
       
   178     // bail out early if there is no change in state
       
   179     if (this.enabled == enabled) {
       
   180         return;
       
   181     }
       
   182     
       
   183     // set the enabled state
       
   184     this.enabled = enabled;
       
   185     
       
   186     // is this a foldable content?
       
   187     if (this.foldable) {
       
   188          // the caption link must be disabled
       
   189         if (this.enabled) {
       
   190             // diabled -> enabled
       
   191             this.foldToggleElement.removeChild(this.captionTextElement);
       
   192             this.foldToggleElement.appendChild(this.captionLinkElement);
       
   193             this.captionLinkElement.appendChild(this.captionTextElement);
       
   194         } else {
       
   195             // enabled -> diabled
       
   196             this.captionLinkElement.removeChild(this.captionTextElement);
       
   197             this.foldToggleElement.removeChild(this.captionLinkElement);
       
   198             this.foldToggleElement.appendChild(this.captionTextElement);
       
   199         }
       
   200     }
       
   201     
       
   202     // update style
       
   203     this.updateStyleFromState();    
       
   204 }
       
   205 
       
   206 // Returns the caption; null if none.
       
   207 ContentPanel.prototype.getCaption = function() {
       
   208     return this.caption;
       
   209 }
       
   210 
       
   211 // Sets the caption; null if none.
       
   212 ContentPanel.prototype.setCaption = function(caption) {
       
   213     // bail out if the caption text element has not been created
       
   214     // this is to prevent the superclass init calling this before
       
   215     // we've initialized our custom caption
       
   216     if (this.captionTextElement == null)
       
   217         return;
       
   218     
       
   219     uiLogger.debug("ContentPanel.setCaption(" + caption + ")");
       
   220     
       
   221     // set the display style
       
   222     this.captionElement.style.display = (caption == null) ? "none" : "block";
       
   223     
       
   224     // set the caption
       
   225     this.caption = caption;
       
   226     this.captionTextElement.innerHTML = (caption == null) ? "" : caption;
       
   227     
       
   228     // update style
       
   229     this.updateStyleFromState();
       
   230 }
       
   231 
       
   232 // Returns the content.
       
   233 ContentPanel.prototype.getContent = function() {
       
   234     return this.contentElement.innerHTML;
       
   235 }
       
   236 
       
   237 // Sets the content.
       
   238 ContentPanel.prototype.setContent = function(content) {
       
   239     uiLogger.debug("ContentPanel.setContent(" + content + ")");
       
   240     this.contentElement.innerHTML = (content == null) ? "" : content;
       
   241 }
       
   242 
       
   243 // Returns the focusable state for the control.
       
   244 ContentPanel.prototype.isFocusable = function() {
       
   245     // a content panel is focusable if it's foldable and enabled
       
   246     return (this.foldable && this.enabled);
       
   247 }
       
   248 
       
   249 // Sets the focused state for the control.
       
   250 // Note: This may not always succeed.
       
   251 ContentPanel.prototype.setFocused = function(focused) {
       
   252     uiLogger.debug("ContentPanel.setFocused(" + focused + ")");
       
   253     if (this.enabled && this.foldable) {
       
   254         if (focused) {
       
   255             this.captionLinkElement.focus();
       
   256         } else {
       
   257             this.captionLinkElement.blur();
       
   258         }
       
   259     }
       
   260     // note that this.focused gets set as a result of focusStateChanged() being called
       
   261     // rather than setting it explicitly here
       
   262 }
       
   263 
       
   264 // Returns the expanded state.
       
   265 ContentPanel.prototype.isExpanded = function() {
       
   266     return this.expanded;
       
   267 }
       
   268 
       
   269 // Sets the expanded state.
       
   270 ContentPanel.prototype.setExpanded = function(expanded) {
       
   271     uiLogger.debug("ContentPanel.setExpanded(" + expanded + ")");
       
   272     
       
   273     // make sure only foldable content panels are folded
       
   274     if (!this.foldable) {
       
   275         uiLogger.warn("Cannot fold a non-foldable content panel!");
       
   276         return;
       
   277     }
       
   278     
       
   279     this.expanded = expanded;
       
   280     if (this.expanded) {
       
   281         // expand
       
   282         this.contentElement.style.display = "block";
       
   283         
       
   284         // find out control top and bottom
       
   285         var controlTop = this.getAbsoluteTop(this.rootElement);
       
   286         var controlHeight = this.rootElement.clientHeight;
       
   287         var controlBottom = controlTop + controlHeight;
       
   288         
       
   289         // find out the viewport top and bottom
       
   290         var viewportTop = window.scrollY;
       
   291         var viewportHeight = window.innerHeight;
       
   292         var viewportBottom = viewportTop + viewportHeight;
       
   293         
       
   294         // make sure the control is positioned so that it can be seen
       
   295         var overflow = controlBottom - viewportBottom;
       
   296         if (overflow > 0) {
       
   297             // there's overflow so we need to scroll to get the control
       
   298             // into the viewport - however not so far that the control
       
   299             // goes past the viewport top.
       
   300             var distanceToTop = controlTop - viewportTop;
       
   301             var scrollAmount = Math.min(overflow, distanceToTop);
       
   302             window.scrollBy(0, scrollAmount);
       
   303         }
       
   304     } else {
       
   305         // collapse
       
   306         this.contentElement.style.display = "none";
       
   307     }
       
   308     
       
   309     // notify event listeners
       
   310     this.fireEvent(this.createEvent("ExpandedStateChanged", this.expanded));
       
   311     
       
   312     // update the style
       
   313     this.updateStyleFromState();
       
   314 }
       
   315 
       
   316 // Returns the absolute position (y) of the given element.
       
   317 ContentPanel.prototype.getAbsoluteTop = function(element) {
       
   318     // traverse from element to root and add top-offset
       
   319     // for each element we find on the way
       
   320     var absTop = 0;
       
   321     while (element != null) {
       
   322         absTop += element.offsetTop;
       
   323         element = element.offsetParent;
       
   324     }
       
   325     return absTop;
       
   326 }
       
   327 
       
   328 // Callback for when the caption is clicked.
       
   329 ContentPanel.prototype.captionClicked = function() {
       
   330     uiLogger.debug("ContentPanel.captionClicked()");
       
   331     
       
   332     // if we're enabled then a click results toggling the expanded state
       
   333     if (this.enabled) {
       
   334         // focus when clicked
       
   335         if (!this.focused) {
       
   336             this.captionLinkElement.focus();
       
   337         }
       
   338         
       
   339         // toggle the expanded state
       
   340         this.setExpanded(!this.expanded);
       
   341     }
       
   342 }
       
   343 
       
   344 // Updates the style of the control to reflects the state of the control.
       
   345 ContentPanel.prototype.updateStyleFromState = function() {
       
   346     uiLogger.debug("ContentPanel.updateStyleFromState()");
       
   347 
       
   348     // determine the state name
       
   349     var stateName = this.getStyleStateName();
       
   350     
       
   351     // set root element class name
       
   352     this.setClassName(this.rootElement, "Control");
       
   353 
       
   354     // set the control assembly class names
       
   355     this.setClassName(this.assemblyElement, "ControlAssembly ControlAssembly" + stateName);
       
   356     
       
   357     if (this.foldable) {
       
   358         // foldable content panel
       
   359         this.setClassName(this.captionElement, "ContentPanelCaptionFoldable");
       
   360         this.setClassName(this.foldToggleElement, "ContentPanelFoldToggle ContentPanelFoldToggle" + (this.expanded ? "Expanded" : "Collapsed"));
       
   361     } else {
       
   362         // non-folding
       
   363         this.setClassName(this.captionElement, "ContentPanelCaptionNonFoldable");
       
   364     }
       
   365     
       
   366     // set the content caption text class names
       
   367     this.setClassName(this.captionTextElement, "ContentPanelCaptionText ContentPanelCaptionText" + stateName);
       
   368     
       
   369     // set the content element class names
       
   370     this.setClassName(this.contentElement, "ContentPanelContent");
       
   371 	
       
   372 }