]> git.wh0rd.org - tt-rss.git/blame - lib/dijit/layout/AccordionContainer.js.uncompressed.js
modify dojo rebuild script to remove uncompressed files
[tt-rss.git] / lib / dijit / layout / AccordionContainer.js.uncompressed.js
CommitLineData
f0cfe83e
AD
1require({cache:{
2'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"}});
3define("dijit/layout/AccordionContainer", [
4 "require",
5 "dojo/_base/array", // array.forEach array.map
6 "dojo/_base/declare", // declare
7 "dojo/_base/event", // event.stop
8 "dojo/_base/fx", // fx.Animation
9 "dojo/dom", // dom.setSelectable
10 "dojo/dom-attr", // domAttr.attr
11 "dojo/dom-class", // domClass.remove
12 "dojo/dom-construct", // domConstruct.place
13 "dojo/dom-geometry",
14 "dojo/keys", // keys
15 "dojo/_base/lang", // lang.getObject lang.hitch
16 "dojo/sniff", // has("ie") has("dijit-legacy-requires")
17 "dojo/topic", // publish
18 "../focus", // focus.focus()
19 "../_base/manager", // manager.defaultDuration
20 "dojo/ready",
21 "../_Widget",
22 "../_Container",
23 "../_TemplatedMixin",
24 "../_CssStateMixin",
25 "./StackContainer",
26 "./ContentPane",
27 "dojo/text!./templates/AccordionButton.html"
28], function(require, array, declare, event, fx, dom, domAttr, domClass, domConstruct, domGeometry,
29 keys, lang, has, topic, focus, manager, ready,
30 _Widget, _Container, _TemplatedMixin, _CssStateMixin, StackContainer, ContentPane, template){
31
32 // module:
33 // dijit/layout/AccordionContainer
34
35
36 // Design notes:
37 //
38 // An AccordionContainer is a StackContainer, but each child (typically ContentPane)
39 // is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
40 //
41 // The resulting markup will look like:
42 //
43 // <div class=dijitAccordionContainer>
44 // <div class=dijitAccordionInnerContainer> (one pane)
45 // <div class=dijitAccordionTitle> (title bar) ... </div>
46 // <div class=dijtAccordionChildWrapper> (content pane) </div>
47 // </div>
48 // </div>
49 //
50 // Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
51 // child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
52 // which on claro has a 1px border plus a 2px bottom margin.
53 //
54 // During animation there are two dijtAccordionChildWrapper's shown, so we need
55 // to compensate for that.
56
57
58 var AccordionButton = declare("dijit.layout._AccordionButton", [_Widget, _TemplatedMixin, _CssStateMixin], {
59 // summary:
60 // The title bar to click to open up an accordion pane.
61 // Internal widget used by AccordionContainer.
62 // tags:
63 // private
64
65 templateString: template,
66
67 // label: String
68 // Title of the pane
69 label: "",
70 _setLabelAttr: {node: "titleTextNode", type: "innerHTML" },
71
72 // title: String
73 // Tooltip that appears on hover
74 title: "",
75 _setTitleAttr: {node: "titleTextNode", type: "attribute", attribute: "title"},
76
77 // iconClassAttr: String
78 // CSS class for icon to left of label
79 iconClassAttr: "",
80 _setIconClassAttr: { node: "iconNode", type: "class" },
81
82 baseClass: "dijitAccordionTitle",
83
84 getParent: function(){
85 // summary:
86 // Returns the AccordionContainer parent.
87 // tags:
88 // private
89 return this.parent;
90 },
91
92 buildRendering: function(){
93 this.inherited(arguments);
94 var titleTextNodeId = this.id.replace(' ','_');
95 domAttr.set(this.titleTextNode, "id", titleTextNodeId+"_title");
96 this.focusNode.setAttribute("aria-labelledby", domAttr.get(this.titleTextNode, "id"));
97 dom.setSelectable(this.domNode, false);
98 },
99
100 getTitleHeight: function(){
101 // summary:
102 // Returns the height of the title dom node.
103 return domGeometry.getMarginSize(this.domNode).h; // Integer
104 },
105
106 // TODO: maybe the parent should set these methods directly rather than forcing the code
107 // into the button widget?
108 _onTitleClick: function(){
109 // summary:
110 // Callback when someone clicks my title.
111 var parent = this.getParent();
112 parent.selectChild(this.contentWidget, true);
113 focus.focus(this.focusNode);
114 },
115
116 _onTitleKeyPress: function(/*Event*/ evt){
117 return this.getParent()._onKeyPress(evt, this.contentWidget);
118 },
119
120 _setSelectedAttr: function(/*Boolean*/ isSelected){
121 this._set("selected", isSelected);
122 this.focusNode.setAttribute("aria-expanded", isSelected ? "true" : "false");
123 this.focusNode.setAttribute("aria-selected", isSelected ? "true" : "false");
124 this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
125 }
126 });
127
128 var AccordionInnerContainer = declare("dijit.layout._AccordionInnerContainer", [_Widget, _CssStateMixin], {
129 // summary:
130 // Internal widget placed as direct child of AccordionContainer.containerNode.
131 // When other widgets are added as children to an AccordionContainer they are wrapped in
132 // this widget.
133
134/*=====
135 // buttonWidget: Function|String
136 // Class to use to instantiate title
137 // (Wish we didn't have a separate widget for just the title but maintaining it
138 // for backwards compatibility, is it worth it?)
139 buttonWidget: null,
140=====*/
141
142/*=====
143 // contentWidget: dijit/_WidgetBase
144 // Pointer to the real child widget
145 contentWidget: null,
146=====*/
147
148 baseClass: "dijitAccordionInnerContainer",
149
150 // tell nested layout widget that we will take care of sizing
151 isLayoutContainer: true,
152
153 buildRendering: function(){
154 // Builds a template like:
155 // <div class=dijitAccordionInnerContainer>
156 // Button
157 // <div class=dijitAccordionChildWrapper>
158 // ContentPane
159 // </div>
160 // </div>
161
162 // Create wrapper div, placed where the child is now
163 this.domNode = domConstruct.place("<div class='" + this.baseClass +
164 "' role='presentation'>", this.contentWidget.domNode, "after");
165
166 // wrapper div's first child is the button widget (ie, the title bar)
167 var child = this.contentWidget,
168 cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
169 this.button = child._buttonWidget = (new cls({
170 contentWidget: child,
171 label: child.title,
172 title: child.tooltip,
173 dir: child.dir,
174 lang: child.lang,
175 textDir: child.textDir,
176 iconClass: child.iconClass,
177 id: child.id + "_button",
178 parent: this.parent
179 })).placeAt(this.domNode);
180
181 // and then the actual content widget (changing it from prior-sibling to last-child),
182 // wrapped by a <div class=dijitAccordionChildWrapper>
183 this.containerNode = domConstruct.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode);
184 domConstruct.place(this.contentWidget.domNode, this.containerNode);
185 },
186
187 postCreate: function(){
188 this.inherited(arguments);
189
190 // Map changes in content widget's title etc. to changes in the button
191 var button = this.button;
192 this._contentWidgetWatches = [
193 this.contentWidget.watch('title', lang.hitch(this, function(name, oldValue, newValue){
194 button.set("label", newValue);
195 })),
196 this.contentWidget.watch('tooltip', lang.hitch(this, function(name, oldValue, newValue){
197 button.set("title", newValue);
198 })),
199 this.contentWidget.watch('iconClass', lang.hitch(this, function(name, oldValue, newValue){
200 button.set("iconClass", newValue);
201 }))
202 ];
203 },
204
205 _setSelectedAttr: function(/*Boolean*/ isSelected){
206 this._set("selected", isSelected);
207 this.button.set("selected", isSelected);
208 if(isSelected){
209 var cw = this.contentWidget;
210 if(cw.onSelected){ cw.onSelected(); }
211 }
212 },
213
214 startup: function(){
215 // Called by _Container.addChild()
216 this.contentWidget.startup();
217 },
218
219 destroy: function(){
220 this.button.destroyRecursive();
221
222 array.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
223
224 delete this.contentWidget._buttonWidget;
225 delete this.contentWidget._wrapperWidget;
226
227 this.inherited(arguments);
228 },
229
230 destroyDescendants: function(/*Boolean*/ preserveDom){
231 // since getChildren isn't working for me, have to code this manually
232 this.contentWidget.destroyRecursive(preserveDom);
233 }
234 });
235
236 var AccordionContainer = declare("dijit.layout.AccordionContainer", StackContainer, {
237 // summary:
238 // Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
239 // and switching between panes is visualized by sliding the other panes up/down.
240 // example:
241 // | <div data-dojo-type="dijit/layout/AccordionContainer">
242 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 1">
243 // | </div>
244 // | <div data-dojo-type="dijit/layout/ContentPane" title="pane 2">
245 // | <p>This is some text</p>
246 // | </div>
247 // | </div>
248
249 // duration: Integer
250 // Amount of time (in ms) it takes to slide panes
251 duration: manager.defaultDuration,
252
253 // buttonWidget: [const] String
254 // The name of the widget used to display the title of each pane
255 buttonWidget: AccordionButton,
256
257/*=====
258 // _verticalSpace: Number
259 // Pixels of space available for the open pane
260 // (my content box size minus the cumulative size of all the title bars)
261 _verticalSpace: 0,
262=====*/
263 baseClass: "dijitAccordionContainer",
264
265 buildRendering: function(){
266 this.inherited(arguments);
267 this.domNode.style.overflow = "hidden"; // TODO: put this in dijit.css
268 this.domNode.setAttribute("role", "tablist"); // TODO: put this in template
269 },
270
271 startup: function(){
272 if(this._started){ return; }
273 this.inherited(arguments);
274 if(this.selectedChildWidget){
275 this.selectedChildWidget._wrapperWidget.set("selected", true);
276 }
277 },
278
279 layout: function(){
280 // Implement _LayoutWidget.layout() virtual method.
281 // Set the height of the open pane based on what room remains.
282
283 var openPane = this.selectedChildWidget;
284
285 if(!openPane){ return;}
286
287 // space taken up by title, plus wrapper div (with border/margin) for open pane
288 var wrapperDomNode = openPane._wrapperWidget.domNode,
289 wrapperDomNodeMargin = domGeometry.getMarginExtents(wrapperDomNode),
290 wrapperDomNodePadBorder = domGeometry.getPadBorderExtents(wrapperDomNode),
291 wrapperContainerNode = openPane._wrapperWidget.containerNode,
292 wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
293 wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
294 mySize = this._contentBox;
295
296 // get cumulative height of all the unselected title bars
297 var totalCollapsedHeight = 0;
298 array.forEach(this.getChildren(), function(child){
299 if(child != openPane){
300 // Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin
301 // to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px
302 // margin below the bottom pane (even though we don't want one).
303 totalCollapsedHeight += domGeometry.getMarginSize(child._wrapperWidget.domNode).h;
304 }
305 });
306 this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h
307 - wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h
308 - openPane._buttonWidget.getTitleHeight();
309
310 // Memo size to make displayed child
311 this._containerContentBox = {
312 h: this._verticalSpace,
313 w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w
314 - wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w
315 };
316
317 if(openPane){
318 openPane.resize(this._containerContentBox);
319 }
320 },
321
322 _setupChild: function(child){
323 // Overrides _LayoutWidget._setupChild().
324 // Put wrapper widget around the child widget, showing title
325
326 child._wrapperWidget = AccordionInnerContainer({
327 contentWidget: child,
328 buttonWidget: this.buttonWidget,
329 id: child.id + "_wrapper",
330 dir: child.dir,
331 lang: child.lang,
332 textDir: child.textDir,
333 parent: this
334 });
335
336 this.inherited(arguments);
337 },
338
339 addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
340 // Overrides _LayoutWidget.addChild().
341 if(this._started){
342 // Adding a child to a started Accordion is complicated because children have
343 // wrapper widgets. Default code path (calling this.inherited()) would add
344 // the new child inside another child's wrapper.
345
346 // First add in child as a direct child of this AccordionContainer
347 var refNode = this.containerNode;
348 if(insertIndex && typeof insertIndex == "number"){
349 var children = _Widget.prototype.getChildren.call(this); // get wrapper panes
350 if(children && children.length >= insertIndex){
351 refNode = children[insertIndex-1].domNode;
352 insertIndex = "after";
353 }
354 }
355 domConstruct.place(child.domNode, refNode, insertIndex);
356
357 if(!child._started){
358 child.startup();
359 }
360
361 // Then stick the wrapper widget around the child widget
362 this._setupChild(child);
363
364 // Code below copied from StackContainer
365 topic.publish(this.id+"-addChild", child, insertIndex); // publish
366 this.layout();
367 if(!this.selectedChildWidget){
368 this.selectChild(child);
369 }
370 }else{
371 // We haven't been started yet so just add in the child widget directly,
372 // and the wrapper will be created on startup()
373 this.inherited(arguments);
374 }
375 },
376
377 removeChild: function(child){
378 // Overrides _LayoutWidget.removeChild().
379
380 // Destroy wrapper widget first, before StackContainer.getChildren() call.
381 // Replace wrapper widget with true child widget (ContentPane etc.).
382 // This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
383 if(child._wrapperWidget){
384 domConstruct.place(child.domNode, child._wrapperWidget.domNode, "after");
385 child._wrapperWidget.destroy();
386 delete child._wrapperWidget;
387 }
388
389 domClass.remove(child.domNode, "dijitHidden");
390
391 this.inherited(arguments);
392 },
393
394 getChildren: function(){
395 // Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
396 return array.map(this.inherited(arguments), function(child){
397 return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
398 }, this);
399 },
400
401 destroy: function(){
402 if(this._animation){
403 this._animation.stop();
404 }
405 array.forEach(this.getChildren(), function(child){
406 // If AccordionContainer has been started, then each child has a wrapper widget which
407 // also needs to be destroyed.
408 if(child._wrapperWidget){
409 child._wrapperWidget.destroy();
410 }else{
411 child.destroyRecursive();
412 }
413 });
414 this.inherited(arguments);
415 },
416
417 _showChild: function(child){
418 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
419 child._wrapperWidget.containerNode.style.display="block";
420 return this.inherited(arguments);
421 },
422
423 _hideChild: function(child){
424 // Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
425 child._wrapperWidget.containerNode.style.display="none";
426 this.inherited(arguments);
427 },
428
429 _transition: function(/*dijit/_WidgetBase?*/ newWidget, /*dijit/_WidgetBase?*/ oldWidget, /*Boolean*/ animate){
430 // Overrides StackContainer._transition() to provide sliding of title bars etc.
431
432 if(has("ie") < 8){
433 // workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
434 animate = false;
435 }
436
437 if(this._animation){
438 // there's an in-progress animation. speedily end it so we can do the newly requested one
439 this._animation.stop(true);
440 delete this._animation;
441 }
442
443 var self = this;
444
445 if(newWidget){
446 newWidget._wrapperWidget.set("selected", true);
447
448 var d = this._showChild(newWidget); // prepare widget to be slid in
449
450 // Size the new widget, in case this is the first time it's being shown,
451 // or I have been resized since the last time it was shown.
452 // Note that page must be visible for resizing to work.
453 if(this.doLayout && newWidget.resize){
454 newWidget.resize(this._containerContentBox);
455 }
456 }
457
458 if(oldWidget){
459 oldWidget._wrapperWidget.set("selected", false);
460 if(!animate){
461 this._hideChild(oldWidget);
462 }
463 }
464
465 if(animate){
466 var newContents = newWidget._wrapperWidget.containerNode,
467 oldContents = oldWidget._wrapperWidget.containerNode;
468
469 // During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
470 // which on claro takes up 4px extra space (compared to stable AccordionContainer).
471 // Have to compensate for that by immediately shrinking the pane being closed.
472 var wrapperContainerNode = newWidget._wrapperWidget.containerNode,
473 wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
474 wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
475 animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h;
476
477 oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px";
478
479 this._animation = new fx.Animation({
480 node: newContents,
481 duration: this.duration,
482 curve: [1, this._verticalSpace - animationHeightOverhead - 1],
483 onAnimate: function(value){
484 value = Math.floor(value); // avoid fractional values
485 newContents.style.height = value + "px";
486 oldContents.style.height = (self._verticalSpace - animationHeightOverhead - value) + "px";
487 },
488 onEnd: function(){
489 delete self._animation;
490 newContents.style.height = "auto";
491 oldWidget._wrapperWidget.containerNode.style.display = "none";
492 oldContents.style.height = "auto";
493 self._hideChild(oldWidget);
494 }
495 });
496 this._animation.onStop = this._animation.onEnd;
497 this._animation.play();
498 }
499
500 return d; // If child has an href, promise that fires when the widget has finished loading
501 },
502
503 // note: we are treating the container as controller here
504 _onKeyPress: function(/*Event*/ e, /*dijit/_WidgetBase*/ fromTitle){
505 // summary:
506 // Handle keypress events
507 // description:
508 // This is called from a handler on AccordionContainer.domNode
509 // (setup in StackContainer), and is also called directly from
510 // the click handler for accordion labels
511 if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
512 return;
513 }
514 var c = e.charOrCode;
515 if((fromTitle && (c == keys.LEFT_ARROW || c == keys.UP_ARROW)) ||
516 (e.ctrlKey && c == keys.PAGE_UP)){
517 this._adjacent(false)._buttonWidget._onTitleClick();
518 event.stop(e);
519 }else if((fromTitle && (c == keys.RIGHT_ARROW || c == keys.DOWN_ARROW)) ||
520 (e.ctrlKey && (c == keys.PAGE_DOWN || c == keys.TAB))){
521 this._adjacent(true)._buttonWidget._onTitleClick();
522 event.stop(e);
523 }
524 }
525 });
526
527 // Back compat w/1.6, remove for 2.0
528 if(has("dijit-legacy-requires")){
529 ready(0, function(){
530 var requires = ["dijit/layout/AccordionPane"];
531 require(requires); // use indirection so modules not rolled into a build
532 });
533 }
534
535 // For monkey patching
536 AccordionContainer._InnerContainer = AccordionInnerContainer;
537 AccordionContainer._Button = AccordionButton;
538
539 return AccordionContainer;
540});