1 /*
  2  * JBoss, Home of Professional Open Source
  3  * Copyright ${year}, Red Hat, Inc. and individual contributors
  4  * by the @authors tag. See the copyright.txt in the distribution for a
  5  * full listing of individual contributors.
  6  * 
  7  * This is free software; you can redistribute it and/or modify it
  8  * under the terms of the GNU Lesser General Public License as
  9  * published by the Free Software Foundation; either version 2.1 of
 10  * the License, or (at your option) any later version.
 11  * 
 12  * This software is distributed in the hope that it will be useful,
 13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15  * Lesser General Public License for more details.
 16  * 
 17  * You should have received a copy of the GNU Lesser General Public
 18  * License along with this software; if not, write to the Free
 19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 21  */
 22 
 23 
 24 (function ($, rf) {
 25 
 26     rf.ui = rf.ui || {};
 27 
 28     var __DEFAULT_OPTIONS = {
 29         expanded : false,
 30         stylePrefix : "rf-pm-gr",
 31         expandEvent: "click",
 32         collapseEvent: "click",
 33 
 34         // TODO we should use selectionType = {none, selectable, unselectable}
 35         selectable : false,
 36         unselectable : false // unselectable can be only selectable item => if selectable == false than unselectable = false
 37     };
 38 
 39     var EXPAND_ITEM = {
 40 
 41         /**
 42          *
 43          * @return {void}
 44          * */
 45         exec : function (group, expand) {
 46             var mode = group.mode;
 47             if (mode == "server") {
 48                 return this.execServer(group);
 49             } else if (mode == "ajax") {
 50                 return this.execAjax(group);
 51             } else if (mode == "client" || mode == "none") {
 52                 return this.execClient(group, expand);
 53             } else {
 54                 rf.log.error("EXPAND_ITEM.exec : unknown mode (" + mode + ")");
 55             }
 56         },
 57 
 58         /**
 59          * @protected
 60          *
 61          * @return {Boolean} false
 62          * */
 63         execServer : function (group) {
 64             group.__changeState();
 65             rf.submitForm(this.__getParentForm(group), group.options["ajax"]["parameters"] || {});
 66 
 67             return false;
 68         },
 69 
 70         /**
 71          * @protected
 72          *
 73          * @return {Boolean} false
 74          * */
 75         execAjax : function (group) {
 76             var oldState = group.__changeState();
 77             rf.ajax(group.id, null, $.extend({}, group.options["ajax"], {}));
 78             group.__restoreState(oldState);
 79 
 80             return true;
 81         },
 82 
 83         /**
 84          * @protected
 85          *
 86          * @param {PanelMenuGroup} group
 87          * @param {Boolean} expand
 88          * @return {undefined}
 89          *             - false - if process has been terminated
 90          *             - true  - in other cases
 91          * */
 92         execClient : function (group, expand) {
 93             if (expand) {
 94                 group.__expand();
 95             } else {
 96                 group.__collapse();
 97             }
 98 
 99             return group.__fireEvent("switch");
100         },
101 
102         /**
103          * @private
104          * */
105         __getParentForm : function (item) {
106             return $($(rf.getDomElement(item.id)).parents("form")[0]);
107         }
108     };
109 
110     rf.ui.PanelMenuGroup = rf.ui.PanelMenuItem.extendClass({
111             // class name
112             name:"PanelMenuGroup",
113 
114             /**
115              * @class PanelMenuGroup
116              * @name PanelMenuGroup
117              *
118              * @constructor
119              * @param {String} componentId - component id
120              * @param {Hash} options - params
121              * */
122             init : function (componentId, options) {
123                 $super.constructor.call(this, componentId, $.extend({}, __DEFAULT_OPTIONS, options || {}));
124 
125                 this.options.bubbleSelection = this.__rfPanelMenu().options.bubbleSelection;
126                 this.options.expandSingle = this.__rfPanelMenu().options.expandSingle;
127 
128                 if (!this.options.disabled) {
129                     var menuGroup = this;
130 
131                     if (!this.options.selectable) {
132 
133                         //TODO nick - this can be replaced by jQuery.delegate on menu itself
134                         if (this.options.expandEvent == this.options.collapseEvent) {
135                             this.__header().bind(this.options.expandEvent, function () {
136                                 menuGroup.switchExpantion();
137                             });
138 
139                         } else {
140                             this.__header().bind(this.options.expandEvent, function () {
141                                 if (menuGroup.collapsed()) {
142                                     return menuGroup.expand();
143                                 }
144                             });
145 
146                             this.__header().bind(this.options.collapseEvent, function () {
147                                 if (menuGroup.expanded()) {
148                                     return menuGroup.collapse();
149                                 }
150                             });
151                         }
152                     } else {
153 
154                         if (this.options.expandEvent == this.options.collapseEvent) {
155                             if (this.options.expandEvent != 'click') {
156                                 this.__header().bind(this.options.expandEvent, function () {
157                                     menuGroup.switchExpantion();
158                                 });
159                             }
160 
161                         } else {
162                             if (this.options.expandEvent != 'click') {
163                                 this.__header().bind(this.options.expandEvent, function () {
164                                     if (menuGroup.collapsed()) {
165                                         return menuGroup.expand();
166                                     }
167                                 });
168                             }
169 
170                             if (this.options.collapseEvent != 'click') {
171                                 this.__header().bind(this.options.collapseEvent, function () {
172                                     if (menuGroup.expanded()) {
173                                         return menuGroup.collapse();
174                                     }
175                                 });
176                             }
177                         }
178 
179                     }
180 
181                     if (this.options.selectable || this.options.bubbleSelection) {
182                         this.__content().bind("select", function (event) {
183                             if (menuGroup.options.selectable && menuGroup.__isMyEvent(event)) {
184                                 menuGroup.expand();
185                             }
186 
187                             if (menuGroup.options.bubbleSelection && !menuGroup.__isMyEvent(event)) {
188                                 menuGroup.__select();
189                                 if (!menuGroup.expanded()) {
190                                     menuGroup.expand();
191                                 }
192                             }
193                         });
194 
195                         this.__content().bind("unselect", function (event) {
196                             if (menuGroup.options.selectable && menuGroup.__isMyEvent(event)) {
197                                 menuGroup.collapse();
198                             }
199 
200                             if (menuGroup.options.bubbleSelection && !menuGroup.__isMyEvent(event)) {
201                                 menuGroup.__unselect();
202                             }
203                         });
204                     }
205 
206                     /*this.__addUserEventHandler("beforecollapse");
207                      this.__addUserEventHandler("collapse");
208                      this.__addUserEventHandler("beforeexpand");
209                      this.__addUserEventHandler("expand");
210                      this.__addUserEventHandler("beforeswitch");
211                      this.__addUserEventHandler("switch");*/
212                 }
213             },
214 
215             /***************************** Public Methods  ****************************************************************/
216             expanded : function () {
217                 // TODO check invariant in dev mode
218                 // return this.__content().hasClass("rf-pm-exp")
219                 return this.__getExpandValue();
220             },
221 
222             expand : function () {
223                 if (this.expanded()) return;
224                 if (!this.__fireEvent("beforeexpand")) {
225                     return false;
226                 }
227 
228                 EXPAND_ITEM.exec(this, true);
229             },
230 
231             __expand : function () {
232                 this.__updateStyles(true);
233                 this.__collapseForExpandSingle();
234 
235                 return this.__fireEvent("expand");
236             },
237 
238             collapsed : function () {
239                 // TODO check invariant in dev mode
240                 // return this.__content().hasClass("rf-pm-colps")
241                 return !this.__getExpandValue();
242             },
243 
244             collapse : function () {
245                 if (!this.expanded()) return;
246                 if (!this.__fireEvent("beforecollapse")) {
247                     return false;
248                 }
249 
250                 EXPAND_ITEM.exec(this, false);
251             },
252 
253             __collapse : function () {
254                 this.__updateStyles(false);
255 
256                 this.__childGroups().each(function(index, group) {
257                     //TODO nick - why not group.collapse()?
258                     rf.component(group.id).__collapse();
259                 });
260 
261                 return this.__fireEvent("collapse");
262             },
263 
264             __updateStyles : function (expand) {
265                 if (expand) {
266                     //expand
267                     this.__content().removeClass("rf-pm-colps").addClass("rf-pm-exp");
268                     this.__header().removeClass("rf-pm-hdr-colps").addClass("rf-pm-hdr-exp");
269 
270                     this.__setExpandValue(true);
271                 } else {
272                     this.__content().addClass("rf-pm-colps").removeClass("rf-pm-exp");
273                     this.__header().addClass("rf-pm-hdr-colps").removeClass("rf-pm-hdr-exp");
274 
275                     this.__setExpandValue(false);
276                 }
277             },
278 
279             /**
280              * @methodOf
281              * @name PanelMenuGroup#switch
282              *
283              * TODO ...
284              *
285              * @param {boolean} expand
286              * @return {void} TODO ...
287              */
288             switchExpantion : function () { // TODO rename
289                 var continueProcess = this.__fireEvent("beforeswitch");
290                 if (!continueProcess) {
291                     return false;
292                 }
293 
294                 if (this.expanded()) {
295                     this.collapse();
296                 } else {
297                     this.expand();
298                 }
299             },
300 
301             /**
302              * please, remove this method when client side ajax events will be added
303              *
304              * */
305             onCompleteHandler : function () {
306                 if (this.options.selectable) {
307                     $super.onCompleteHandler.call(this);
308                 }
309 
310                 EXPAND_ITEM.execClient(this, this.expanded());
311             },
312 
313             __switch : function (expand) {
314                 if (expand) {
315                     this.__expand();
316                 } else {
317                     this.__collapse();
318                 }
319                 return this.__fireEvent("switch");
320             },
321 
322             /***************************** Private Methods ****************************************************************/
323             __childGroups : function () {
324                 return this.__content().children(".rf-pm-gr")
325             },
326 
327             __group : function () {
328                 return $(rf.getDomElement(this.id))
329             },
330 
331             __header : function () {
332                 return $(rf.getDomElement(this.id + ":hdr"))
333             },
334 
335             __content : function () {
336                 return $(rf.getDomElement(this.id + ":cnt"))
337             },
338 
339             __expandValueInput : function () {
340                 return document.getElementById(this.id + ":expanded");
341             },
342 
343             __getExpandValue : function () {
344                 return this.__expandValueInput().value == "true";
345             },
346 
347             __collapseForExpandSingle: function() {
348                 if (this.options.expandSingle) {
349                     this.__rfPanelMenu().__collapseGroups(this);
350                 }
351             },
352 
353             /**
354              * @methodOf
355              * @name PanelMenuGroup#__setExpandValue
356              *
357              * @param {boolean} value - is group expanded?
358              * @return {boolean} preview value
359              */
360             __setExpandValue : function (value) {
361                 var input = this.__expandValueInput();
362                 var oldValue = input.value;
363 
364                 input.value = value;
365 
366                 return oldValue;
367             },
368 
369             __changeState : function () {
370                 if (!this.__getExpandValue()) {
371                     this.__collapseForExpandSingle();
372                 }
373 
374                 var state = {};
375                 state["expanded"] = this.__setExpandValue(!this.__getExpandValue());
376                 if (this.options.selectable) {
377                     state["itemName"] = this.__rfPanelMenu().selectedItem(this.itemName); // TODO bad function name for function which change component state
378                 }
379 
380                 return state;
381             },
382 
383             __restoreState : function (state) {
384                 if (!state) {
385                     return;
386                 }
387 
388                 if (state["expanded"]) {
389                     this.__setExpandValue(state["expanded"]);
390                 }
391 
392                 if (state["itemName"]) {
393                     this.__rfPanelMenu().selectedItem(state["itemName"]);
394                 }
395             },
396 
397             __isMyEvent: function (event) {
398                 return this.id == event.target.id;
399             },
400 
401             destroy: function () {
402                 rf.Event.unbindById(this.id, "." + this.namespace);
403 
404                 $super.destroy.call(this);
405             }
406         });
407 
408     // define super class link
409     var $super = rf.ui.PanelMenuGroup.$super;
410 })(RichFaces.jQuery, RichFaces);
411