Project

General

Profile

Statistics
| Branch: | Tag: | Revision:

vigigraph / vigigraph / public / js / libs / jxlib.js @ 2832ea2b

History | View | Annotate | Download (297 KB)

1
/******************************************************************************
2
 * MooTools 1.2.2
3
 * Copyright (c) 2006-2007 [Valerio Proietti](http://mad4milk.net/).
4
 * MooTools is distributed under an MIT-style license.
5
 ******************************************************************************
6
 * reset.css - Copyright (c) 2006, Yahoo! Inc. All rights reserved.
7
 * Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt
8
 ******************************************************************************
9
 * Jx UI Library, 2.0.1
10
 * Copyright (c) 2006-2008, DM Solutions Group Inc. All rights reserved.
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a
13
 * copy of this software and associated documentation files (the "Software"),
14
 * to deal in the Software without restriction, including without limitation
15
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16
 * and/or sell copies of the Software, and to permit persons to whom the
17
 * Software is furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included
20
 * in all copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
 * DEALINGS IN THE SOFTWARE.
29
 *****************************************************************************/
30
// $Id: common.js 423 2009-05-12 12:37:56Z pagameba $
31
/**
32
 * Class: Jx
33
 * Jx is a global singleton object that contains the entire Jx library
34
 * within it.  All Jx functions, attributes and classes are accessed
35
 * through the global Jx object.  Jx should not create any other
36
 * global variables, if you discover that it does then please report
37
 * it as a bug
38
 *
39
 * License: 
40
 * Copyright (c) 2008, DM Solutions Group Inc.
41
 * 
42
 * This file is licensed under an MIT style license
43
 */
44
 
45
/* firebug console supressor for IE/Safari/Opera */
46
window.addEvent('load', function() {
47
    if (!("console" in window) || !("firebug" in window.console)) {
48
        var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
49
        "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
50

    
51
        window.console = {};
52
        for (var i = 0; i < names.length; ++i) {
53
            window.console[names[i]] = function() {};
54
        }
55
    }
56
});
57
/* inspired by extjs, apparently removes css image flicker and related problems in IE 6 */
58
/* This is already done in mootools Source/Core/Browser.js  KASI*/
59
/*
60
(function() {
61
    var ua = navigator.userAgent.toLowerCase();
62
    var isIE = ua.indexOf("msie") > -1,
63
        isIE7 = ua.indexOf("msie 7") > -1;
64
    if(isIE && !isIE7) {
65
        try {
66
            document.execCommand("BackgroundImageCache", false, true);
67
        } catch(e) {}
68
    }    
69
})();
70
*/
71
Class.Mutators.Family = function(self,name) {
72
    if ($defined(name)){
73
        self.$family = {'name': name};
74
        $[name] = $.object;
75
        return self;
76
    }
77
    else {
78
        this.implement('$family',{'name':self});
79
    }
80
};
81

    
82
/* Setup global namespace
83
 * If jxcore is loaded by jx.js, then the namespace and baseURL are
84
 * already established
85
 */
86
if (typeof Jx == 'undefined') {
87
    var Jx = {};
88
    (function() {
89
        var aScripts = document.getElementsByTagName('SCRIPT');
90
        for (var i=0; i<aScripts.length; i++) {
91
            var s = aScripts[i].src;
92
            var matches = /(.*[jx|js|lib])\/jxlib(.*)/.exec(s);
93
            if (matches && matches[0]) {
94
                /**
95
                 * Property: {String} baseURL
96
                 * This is the URL that Jx was loaded from, it is 
97
                 * automatically calculated from the script tag
98
                 * src property that included Jx.
99
                 *
100
                 * Note that this assumes that you are loading Jx
101
                 * from a js/ or lib/ folder in parallel to the
102
                 * images/ folder that contains the various images
103
                 * needed by Jx components.  If you have a different
104
                 * folder structure, you can define Jx's base
105
                 * by including the following before including
106
                 * the jxlib javascript file:
107
                 *
108
                 * (code)
109
                 * Jx = {
110
                 *    baseURL: 'some/path'
111
                 * }
112
                 * (end)
113
                 */ 
114
                 Jx.aPixel = document.createElement('img', {alt:'',title:''});
115
                 Jx.aPixel.src = matches[1]+'/a_pixel.png';
116
                 Jx.baseURL = Jx.aPixel.src.substring(0,
117
                     Jx.aPixel.src.indexOf('a_pixel.png'));
118
                
119
            }
120
        }
121
       /**
122
        * Determine if we're running in Adobe AIR. If so, determine which sandbox we're in
123
        */
124
        var src = aScripts[0].src;
125
        if (src.contains('app:')){
126
            Jx.isAir = true;
127
        } else {
128
            Jx.isAir = false;
129
        }
130
    })();
131
} 
132

    
133
/**
134
 * Method: applyPNGFilter
135
 *
136
 * Static method that applies the PNG Filter Hack for IE browsers
137
 * when showing 24bit PNG's.  Used automatically for img tags with
138
 * a class of png24.
139
 *
140
 * The filter is applied using a nifty feature of IE that allows javascript to
141
 * be executed as part of a CSS style rule - this ensures that the hack only
142
 * gets applied on IE browsers.
143
 *
144
 * The CSS that triggers this hack is only in the ie6.css files of the various
145
 * themes.
146
 *
147
 * Parameters:
148
 * object {Object} the object (img) to which the filter needs to be applied.
149
 */
150
Jx.applyPNGFilter = function(o)  {
151
   var t=Jx.aPixel.src;
152
   if( o.src != t ) {
153
       var s=o.src;
154
       o.src = t;
155
       o.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+s+"',sizingMethod='scale')";
156
   }
157
};
158

    
159
Jx.imgQueue = [];   //The queue of images to be loaded
160
Jx.imgLoaded = {};  //a hash table of images that have been loaded and cached
161
Jx.imagesLoading = 0; //counter for number of concurrent image loads 
162

    
163
/**
164
 * Method: addToImgQueue
165
 * Request that an image be set to a DOM IMG element src attribute.  This puts 
166
 * the image into a queue and there are private methods to manage that queue
167
 * and limit image loading to 2 at a time.
168
 *
169
 * Parameters:
170
 * obj - {Object} an object containing an element and src
171
 * property, where element is the element to update and src
172
 * is the url to the image.
173
 */
174
Jx.addToImgQueue = function(obj) {
175
    if (Jx.imgLoaded[obj.src]) {
176
        //if this image was already requested (i.e. it's in cache) just set it directly
177
        obj.element.src = obj.src;
178
    } else {
179
        //otherwise stick it in the queue
180
        Jx.imgQueue.push(obj);
181
        Jx.imgLoaded[obj.src] = true;
182
    }
183
    //start the queue management process
184
    Jx.checkImgQueue();
185
};
186

    
187
/**
188
 * Method: checkImgQueue
189
 *
190
 * An internal method that ensures no more than 2 images are loading at a time.
191
 */
192
Jx.checkImgQueue = function() {
193
    while (Jx.imagesLoading < 2 && Jx.imgQueue.length > 0) {
194
        Jx.loadNextImg();
195
    }
196
};
197

    
198
/**
199
 * Method: loadNextImg
200
 *
201
 * An internal method actually populate the DOM element with the image source.
202
 */
203
Jx.loadNextImg = function() {
204
    var obj = Jx.imgQueue.shift();
205
    if (obj) {
206
        ++Jx.imagesLoading;
207
        obj.element.onload = function(){--Jx.imagesLoading; Jx.checkImgQueue();};
208
        obj.element.onerror = function(){--Jx.imagesLoading; Jx.checkImgQueue();};
209
        obj.element.src = obj.src;
210
    }
211
};
212

    
213
/**
214
 * Method: createIframeShim
215
 * Creates a new iframe element that is intended to fill a container
216
 * to mask out other operating system controls (scrollbars, inputs, 
217
 * buttons, etc) when HTML elements are supposed to be above them.
218
 *
219
 * Returns:
220
 * an HTML iframe element that can be inserted into the DOM.
221
 */
222
Jx.createIframeShim = function() {
223
    return new Element('iframe', {
224
        'class':'jxIframeShim',
225
        'scrolling':'no',
226
        'frameborder':0
227
    });
228
};
229
/**
230
 * Method: getNumber
231
 * safely parse a number and return its integer value.  A NaN value 
232
 * returns 0.  CSS size values are also parsed correctly.
233
 *
234
 * Parameters: 
235
 * n - {Mixed} the string or object to parse.
236
 *
237
 * Returns:
238
 * {Integer} the integer value that the parameter represents
239
 */
240
Jx.getNumber = function(n, def) {
241
  var result = n===null||isNaN(parseInt(n,10))?(def||0):parseInt(n,10);
242
  return result;
243
}
244

    
245
/**
246
 * Method: getPageDimensions
247
 * return the dimensions of the browser client area.
248
 *
249
 * Returns:
250
 * {Object} an object containing a width and height property 
251
 * that represent the width and height of the browser client area.
252
 */
253
Jx.getPageDimensions = function() {
254
    return {width: window.getWidth(), height: window.getHeight()};
255
}
256

    
257
/**
258
 * Class: Element
259
 *
260
 * Element is a global object provided by the mootools library.  The
261
 * functions documented here are extensions to the Element object provided
262
 * by Jx to make cross-browser compatibility easier to achieve.  Most of the
263
 * methods are measurement related.
264
 *
265
 * While the code in these methods has been converted to use MooTools methods,
266
 * there may be better MooTools methods to use to accomplish these things.
267
 * Ultimately, it would be nice to eliminate most or all of these and find the
268
 * MooTools equivalent or convince MooTools to add them.
269
 */
270
Element.implement({
271
    /**
272
     * Method: getBoxSizing
273
     * return the box sizing of an element, one of 'content-box' or 
274
     *'border-box'.
275
     *
276
     * Parameters: 
277
     * elem - {Object} the element to get the box sizing of.
278
     *
279
     * Returns:
280
     * {String} the box sizing of the element.
281
     */
282
    getBoxSizing : function() {
283
      var result = 'content-box';
284
      if (Browser.Engine.trident || Browser.Engine.presto) { 
285
          var cm = document["compatMode"];
286
          if (cm == "BackCompat" || cm == "QuirksMode") { 
287
              result = 'border-box'; 
288
          } else {
289
              result = 'content-box'; 
290
        }
291
      } else {
292
          if (arguments.length === 0) {
293
              node = document.documentElement; 
294
          }
295
          var sizing = this.getStyle("-moz-box-sizing");
296
          if (!sizing) { 
297
              sizing = this.getStyle("box-sizing"); 
298
          }
299
          result = (sizing ? sizing : 'content-box');
300
      }
301
      return result;
302
    },
303
    /**
304
     * Method: getContentBoxSize
305
     * return the size of the content area of an element.  This is the size of
306
     * the element less margins, padding, and borders.
307
     *
308
     * Parameters: 
309
     * elem - {Object} the element to get the content size of.
310
     *
311
     * Returns:
312
     * {Object} an object with two properties, width and height, that
313
     * are the size of the content area of the measured element.
314
     */
315
    getContentBoxSize : function() {
316
      var w = this.offsetWidth;
317
      var h = this.offsetHeight;
318
      var padding = this.getPaddingSize();
319
      var border = this.getBorderSize();
320
      w = w - padding.left - padding.right - border.left - border.right;
321
      h = h - padding.bottom - padding.top - border.bottom - border.top;
322
      return {width: w, height: h};
323
    },
324
    /**
325
     * Method: getBorderBoxSize
326
     * return the size of the border area of an element.  This is the size of
327
     * the element less margins.
328
     *
329
     * Parameters: 
330
     * elem - {Object} the element to get the border sizing of.
331
     *
332
     * Returns:
333
     * {Object} an object with two properties, width and height, that
334
     * are the size of the border area of the measured element.
335
     */
336
    getBorderBoxSize: function() {
337
      var w = this.offsetWidth;
338
      var h = this.offsetHeight;
339
      return {width: w, height: h}; 
340
    },
341
    
342
    /**
343
     * Method: getMarginBoxSize
344
     * return the size of the margin area of an element.  This is the size of
345
     * the element plus margins.
346
     *
347
     * Parameters: 
348
     * elem - {Object} the element to get the margin sizing of.
349
     *
350
     * Returns:
351
     * {Object} an object with two properties, width and height, that
352
     * are the size of the margin area of the measured element.
353
     */
354
    getMarginBoxSize: function() {
355
        var margins = this.getMarginSize();
356
        var w = this.offsetWidth + margins.left + margins.right;
357
        var h = this.offsetHeight + margins.top + margins.bottom;
358
        return {width: w, height: h};
359
    },
360
    
361
    /**
362
     * Method: setContentBoxSize
363
     * set either or both of the width and height of an element to
364
     * the provided size.  This function ensures that the content
365
     * area of the element is the requested size and the resulting
366
     * size of the element may be larger depending on padding and
367
     * borders.
368
     *
369
     * Parameters: 
370
     * elem - {Object} the element to set the content area of.
371
     * size - {Object} an object with a width and/or height property that is the size to set
372
     * the content area of the element to.
373
     */
374
    setContentBoxSize : function(size) {
375
        if (this.getBoxSizing() == 'border-box') {
376
            var padding = this.getPaddingSize();
377
            var border = this.getBorderSize();
378
            if (typeof size.width != 'undefined') {
379
                var width = (size.width + padding.left + padding.right + border.left + border.right);
380
                if (width < 0) {
381
                    width = 0;
382
                }
383
                this.style.width = width + 'px';
384
            }
385
            if (typeof size.height != 'undefined') {
386
                var height = (size.height + padding.top + padding.bottom + border.top + border.bottom);
387
                if (height < 0) {
388
                    height = 0;
389
                }
390
                this.style.height = height + 'px';
391
            }
392
        } else {
393
            if (typeof size.width != 'undefined') {
394
                this.style.width = size.width + 'px';
395
            }
396
            if (typeof size.height != 'undefined') {
397
                this.style.height = size.height + 'px';
398
            }
399
        }
400
    },
401
    /**
402
     * Method: setBorderBoxSize
403
     * set either or both of the width and height of an element to
404
     * the provided size.  This function ensures that the border
405
     * size of the element is the requested size and the resulting
406
     * content areaof the element may be larger depending on padding and
407
     * borders.
408
     *
409
     * Parameters: 
410
     * elem - {Object} the element to set the border size of.
411
     * size - {Object} an object with a width and/or height property that is the size to set
412
     * the content area of the element to.
413
     */
414
    setBorderBoxSize : function(size) {
415
      if (this.getBoxSizing() == 'content-box') {
416
        var padding = this.getPaddingSize();
417
        var border = this.getBorderSize();
418
        var margin = this.getMarginSize();
419
        if (typeof size.width != 'undefined') {
420
          var width = (size.width - padding.left - padding.right - border.left - border.right - margin.left - margin.right);
421
          if (width < 0) {
422
            width = 0;
423
          }
424
          this.style.width = width + 'px';
425
        }
426
        if (typeof size.height != 'undefined') {
427
          var height = (size.height - padding.top - padding.bottom - border.top - border.bottom - margin.top - margin.bottom);
428
          if (height < 0) {
429
            height = 0;
430
          }
431
          this.style.height = height + 'px';
432
        }
433
      } else {
434
        if (typeof size.width != 'undefined' && size.width >= 0) {
435
          this.style.width = size.width + 'px';
436
        }
437
        if (typeof size.height != 'undefined' && size.height >= 0) {
438
          this.style.height = size.height + 'px';
439
        }
440
      }
441
    },
442
    /**
443
     * Method: getPaddingSize
444
     * returns the padding for each edge of an element
445
     *
446
     * Parameters: 
447
     * elem - {Object} The element to get the padding for.
448
     *
449
     * Returns:
450
     * {Object} an object with properties left, top, right and bottom
451
     * that contain the associated padding values.
452
     */
453
    getPaddingSize : function () {
454
      var l = Jx.getNumber(this.getStyle('padding-left'));
455
      var t = Jx.getNumber(this.getStyle('padding-top'));
456
      var r = Jx.getNumber(this.getStyle('padding-right'));
457
      var b = Jx.getNumber(this.getStyle('padding-bottom'));
458
      return {left:l, top:t, right: r, bottom: b};
459
    },
460
    /**
461
     * Method: getBorderSize
462
     * returns the border size for each edge of an element
463
     *
464
     * Parameters: 
465
     * elem - {Object} The element to get the borders for.
466
     *
467
     * Returns:
468
     * {Object} an object with properties left, top, right and bottom
469
     * that contain the associated border values.
470
     */
471
    getBorderSize : function() {
472
      var l = Jx.getNumber(this.getStyle('border-left-width'));
473
      var t = Jx.getNumber(this.getStyle('border-top-width'));
474
      var r = Jx.getNumber(this.getStyle('border-right-width'));
475
      var b = Jx.getNumber(this.getStyle('border-bottom-width'));
476
      return {left:l, top:t, right: r, bottom: b};
477
    },
478
    /**
479
     * Method: getMarginSize
480
     * returns the margin size for each edge of an element
481
     *
482
     * Parameters: 
483
     * elem - {Object} The element to get the margins for.
484
     *
485
     * Returns:
486
     *: {Object} an object with properties left, top, right and bottom
487
     * that contain the associated margin values.
488
     */
489
    getMarginSize : function() {
490
      var l = Jx.getNumber(this.getStyle('margin-left'));
491
      var t = Jx.getNumber(this.getStyle('margin-top'));
492
      var r = Jx.getNumber(this.getStyle('margin-right'));
493
      var b = Jx.getNumber(this.getStyle('margin-bottom'));
494
      return {left:l, top:t, right: r, bottom: b};
495
    },
496
    
497
    /**
498
     * Method: descendantOf
499
     * determines if the element is a descendent of the reference node.
500
     *
501
     * Parameters:
502
     * node - {HTMLElement} the reference node
503
     *
504
     * Returns:
505
     * {Boolean} true if the element is a descendent, false otherwise.
506
     */
507
    descendantOf: function(node) {
508
        var parent = $(this.parentNode);
509
        while (parent != node && parent && parent.parentNode && parent.parentNode != parent) {
510
            parent = $(parent.parentNode);
511
        }
512
        return parent == node;
513
    },
514
    
515
    /**
516
     * Method: findElement
517
     * search the parentage of the element to find an element of the given
518
     * tag name.
519
     *
520
     * Parameters:
521
     * type - {String} the tag name of the element type to search for
522
     *
523
     * Returns:
524
     * {HTMLElement} the first node (this one or first parent) with the
525
     * requested tag name or false if none are found.
526
     */
527
    findElement: function(type) {
528
        var o = this;
529
        var tagName = o.tagName;
530
        while (o.tagName != type && o && o.parentNode && o.parentNode != o) {
531
            o = $(o.parentNode);
532
        }
533
        return o.tagName == type ? o : false;
534
    }
535
} );
536

    
537
/**
538
 * Class: Jx.ContentLoader
539
 * 
540
 * ContentLoader is a mix-in class that provides a consistent
541
 * mechanism for other Jx controls to load content in one of
542
 * four different ways:
543
 *
544
 * o using an existing element, by id
545
 *
546
 * o using an existing element, by object reference
547
 *
548
 * o using an HTML string
549
 *
550
 * o using a URL to get the content remotely
551
 *
552
 * Use the Implements syntax in your Class to add Jx.ContentLoader
553
 * to your class.
554
 *
555
 * Option: content
556
 * content may be an HTML element reference, the id of an HTML element
557
 * already in the DOM, or an HTML string that becomes the inner HTML of
558
 * the element.
559
 *
560
 * Option: contentURL
561
 * the URL to load content from
562
 */
563
Jx.ContentLoader = new Class ({
564
    /**
565
     * Property: contentIsLoaded
566
     *
567
     * tracks the load state of the content, specifically useful
568
     * in the case of remote content.
569
     */ 
570
    contentIsLoaded: false,
571
    /**
572
     * Method: loadContent
573
     *
574
     * triggers loading of content based on options set for the current
575
     * object.
576
     *
577
     * Parameters: 
578
     * element - {Object} the element to insert the content into
579
     *
580
     * Events:
581
     *
582
     * ContentLoader adds the following events to an object.  You can
583
     * register for these events using the addEvent method or by providing
584
     * callback functions via the on{EventName} properties in the options 
585
     * object
586
     *
587
     * contentLoaded - called when the content has been loaded.  If the
588
     *     content is not asynchronous then this is called before loadContent
589
     *     returns.
590
     * contentLoadFailed - called if the content fails to load, primarily
591
     *     useful when using the contentURL method of loading content.
592
     */     
593
    loadContent: function(element) {
594
        element = $(element);
595
        if (this.options.content) {
596
            var c;
597
            if (this.options.content.domObj) {
598
                c = $(this.options.content.domObj);
599
            } else {
600
                c = $(this.options.content);
601
            }
602
            if (c) {
603
                if (this.options.content.addTo) {
604
                    this.options.content.addTo(element);
605
                } else {
606
                    element.appendChild(c);                    
607
                }
608
                this.contentIsLoaded = true;                
609
            } else {
610
                element.innerHTML = this.options.content;
611
                this.contentIsLoaded = true;
612
            }
613
        } else if (this.options.contentURL) {
614
            this.contentIsLoaded = false;
615
            this.req = new Request({
616
                url: this.options.contentURL, 
617
                method:'get',
618
                evalScripts:true,
619
                onSuccess:(function(html) {
620
                    element.innerHTML = html;
621
                    this.contentIsLoaded = true;
622
                    if (Jx.isAir){
623
                        $clear(this.reqTimeout);
624
                    }
625
                    this.fireEvent('contentLoaded', this);
626
                }).bind(this), 
627
                onFailure: (function(){
628
                    this.contentIsLoaded = true;
629
                    this.fireEvent('contentLoadFailed', this);
630
                }).bind(this),
631
                headers: {'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT'}
632
            });
633
            this.req.send();
634
            if (Jx.isAir) {
635
                var timeout = $defined(this.options.timeout) ? this.options.timeout : 10000;
636
                this.reqTimeout = this.checkRequest.delay(timeout, this);
637
            }
638
        } else {
639
            this.contentIsLoaded = true;
640
        }
641
        if (this.options.contentId) {
642
            element.id = this.options.contentId;
643
        }
644
        if (this.contentIsLoaded) {
645
            this.fireEvent('contentLoaded', this);
646
        }
647
    },
648
    
649
    processContent: function(element) {
650
        $A(element.childNodes).each(function(node){
651
            if (node.tagName == 'INPUT' || node.tagName == 'SELECT' || node.tagName == 'TEXTAREA') {
652
                if (node.type == 'button') {
653
                    node.addEvent('click', function(){
654
                        this.fireEvent('click', this, node);
655
                    });
656
                } else {
657
                    node.addEvent('change', function(){
658
                        this.fireEvent('change',node);
659
                    });
660
                }
661
            } else {
662
                if (node.childNodes) {
663
                    this.processContent(node);
664
                }
665
            }
666
        }, this);
667
    }
668
});
669

    
670

    
671
/**
672
 * It seems AIR never returns an XHR that "fails" by not finding the 
673
 * appropriate file when run in the application sandbox and retrieving a local
674
 * file. This affects Jx.ContentLoader in that a "failed" event is never fired. 
675
 * 
676
 * To fix this, I've added a timeout that waits about 10 seconds or so in the code above
677
 * for the XHR to return, if it hasn't returned at the end of the timeout, we cancel the
678
 * XHR and fire the failure event.
679
 *
680
 * This code only gets added if we're in AIR.
681
 */
682
if (Jx.isAir){
683
    Jx.ContentLoader.implement({
684
        /**
685
         * Method: checkRequest()
686
         * Is fired after a delay to check the request to make sure it's not
687
         * failing in AIR.
688
         */
689
        checkRequest: function(){
690
            if (this.req.xhr.readyState === 1) {
691
                //we still haven't gotten the file. Cancel and fire the
692
                //failure
693
                $clear(this.reqTimeout);
694
                this.req.cancel();
695
                this.contentIsLoaded = true;
696
                this.fireEvent('contentLoadFailed', this);
697
            }
698
        }
699
    });
700
}
701

    
702
/**
703
 * Class: Jx.AutoPosition
704
 * Mix-in class that provides a method for positioning
705
 * elements relative to other elements.
706
 */
707
Jx.AutoPosition = new Class({
708
    /**
709
     * Method: position
710
     * positions an element relative to another element
711
     * based on the provided options.  Positioning rules are
712
     * a string with two space-separated values.  The first value
713
     * references the parent element and the second value references
714
     * the thing being positioned.  In general, multiple rules can be
715
     * considered by passing an array of rules to the horizontal and
716
     * vertical options.  The position method will attempt to position
717
     * the element in relation to the relative element using the rules
718
     * specified in the options.  If the element does not fit in the
719
     * viewport using the rule, then the next rule is attempted.  If
720
     * all rules fail, the last rule is used and element may extend
721
     * outside the viewport.  Horizontal and vertical rules are
722
     * processed independently.
723
     *
724
     * Horizontal Positioning:
725
     * Horizontal values are 'left', 'center', 'right', and numeric values.
726
     * Some common rules are:
727
     * o 'left left' is interpreted as aligning the left
728
     * edge of the element to be positioned with the left edge of the
729
     * reference element.  
730
     * o 'right right' aligns the two right edges.  
731
     * o 'right left' aligns the left edge of the element to the right of
732
     * the reference element.  
733
     * o 'left right' aligns the right edge of the element to the left
734
     * edge of the reference element.
735
     *
736
     * Vertical Positioning:
737
     * Vertical values are 'top', 'center', 'bottom', and numeric values.
738
     * Some common rules are:
739
     * o 'top top' is interpreted as aligning the top
740
     * edge of the element to be positioned with the top edge of the
741
     * reference element.  
742
     * o 'bottom bottom' aligns the two bottom edges.  
743
     * o 'bottom top' aligns the top edge of the element to the bottom of
744
     * the reference element.  
745
     * o 'top bottom' aligns the bottom edge of the element to the top
746
     * edge of the reference element.
747
     * 
748
     * Parameters:
749
     * element - the element to position
750
     * relative - the element to position relative to
751
     * options - the positioning options, see list below.
752
     *
753
     * Options:
754
     * horizontal - the horizontal positioning rule to use to position the 
755
     *    element.  Valid values are 'left', 'center', 'right', and a numeric
756
     *    value.  The default value is 'center center'.
757
     * vertical - the vertical positioning rule to use to position the 
758
     *    element.  Valid values are 'top', 'center', 'bottom', and a numeric
759
     *    value.  The default value is 'center center'.
760
     * offsets - an object containing numeric pixel offset values for the object
761
     *    being positioned as top, right, bottom and left properties.
762
     */
763
    position: function(element, relative, options) {
764
        element = $(element);
765
        relative = $(relative);
766
        var hor = $splat(options.horizontal || ['center center']);
767
        var ver = $splat(options.vertical || ['center center']);
768
        var offsets = $merge({top:0,right:0,bottom:0,left:0}, options.offsets || {});
769
        
770
        var coords = relative.getCoordinates(); //top, left, width, height
771
        var page;
772
        var scroll;
773
        if (!$(element.parentNode) || element.parentNode ==  document.body) {
774
            page = Jx.getPageDimensions();
775
            scroll = $(document.body).getScroll();
776
        } else {
777
            page = $(element.parentNode).getContentBoxSize(); //width, height
778
            scroll = $(element.parentNode).getScroll();
779
        }
780
        if (relative == document.body) {
781
            // adjust coords for the scroll offsets to make the object
782
            // appear in the right part of the page.
783
            coords.left += scroll.x;
784
            coords.top += scroll.y;            
785
        } else if (element.parentNode == relative) {
786
            // if the element is opening *inside* its relative, we want
787
            // it to position correctly within it so top/left becomes
788
            // the reference system.
789
            coords.left = 0;
790
            coords.top = 0;
791
        }
792
        var size = element.getMarginBoxSize(); //width, height
793
        var left;
794
        var right;
795
        var top;
796
        var bottom;
797
        var n;
798
        if (!hor.some(function(opt) {
799
            var parts = opt.split(' ');
800
            if (parts.length != 2) {
801
                return false;
802
            }
803
            if (!isNaN(parseInt(parts[0],10))) {
804
                n = parseInt(parts[0],10);
805
                if (n>=0) {
806
                    left = n;                    
807
                } else {
808
                    left = coords.left + coords.width + n;
809
                }
810
            } else {
811
                switch(parts[0]) {
812
                    case 'right':
813
                        left = coords.left + coords.width;
814
                        break;
815
                    case 'center':
816
                        left = coords.left + Math.round(coords.width/2);
817
                        break;
818
                    case 'left':
819
                    default:
820
                        left = coords.left;
821
                        break;
822
                }                
823
            }
824
            if (!isNaN(parseInt(parts[1],10))) {
825
                n = parseInt(parts[1],10);
826
                if (n<0) {
827
                    right = left + n;
828
                    left = right - size.width;
829
                } else {
830
                    left += n;
831
                    right = left + size.width;
832
                }
833
                right = coords.left + coords.width + parseInt(parts[1],10);
834
                left = right - size.width;
835
            } else {
836
                switch(parts[1]) {
837
                    case 'left':
838
                        left -= offsets.left;
839
                        right = left + size.width;
840
                        break;
841
                    case 'right':
842
                        left += offsets.right;
843
                        right = left;
844
                        left = left - size.width;
845
                        break;
846
                    case 'center':
847
                    default:
848
                        left = left - Math.round(size.width/2);
849
                        right = left + size.width;
850
                        break;
851
                }                
852
            }
853
            return (left >= scroll.x && right <= scroll.x + page.width);
854
        })) {
855
            // all failed, snap the last position onto the page as best
856
            // we can - can't do anything if the element is wider than the
857
            // space available.
858
            if (right > page.width) {
859
                left = scroll.x + page.width - size.width;
860
            }
861
            if (left < 0) {
862
                left = 0;
863
            }
864
        }
865
        element.setStyle('left', left);
866
        
867
        if (!ver.some(function(opt) {
868
                var parts = opt.split(' ');
869
                if (parts.length != 2) {
870
                    return false;
871
                }
872
                if (!isNaN(parseInt(parts[0],10))) {
873
                    top = parseInt(parts[0],10);
874
                } else {
875
                    switch(parts[0]) {
876
                        case 'bottom':
877
                            top = coords.top + coords.height;
878
                            break;
879
                        case 'center':
880
                            top = coords.top + Math.round(coords.height/2);
881
                            break;
882
                        case 'top':
883
                        default:
884
                            top = coords.top;
885
                            break;
886
                    }
887
                }
888
                if (!isNaN(parseInt(parts[1],10))) {
889
                    var n = parseInt(parts[1],10);
890
                    if (n>=0) {
891
                        top += n;
892
                        bottom = top + size.height;
893
                    } else {
894
                        bottom = top + n;
895
                        top = bottom - size.height; 
896
                    }
897
                } else {
898
                    switch(parts[1]) {
899
                        case 'top':
900
                            top -= offsets.top;
901
                            bottom = top + size.height;
902
                            break;
903
                        case 'bottom':
904
                            top += offsets.bottom;
905
                            bottom = top;
906
                            top = top - size.height;
907
                            break;
908
                        case 'center':
909
                        default:
910
                            top = top - Math.round(size.height/2);
911
                            bottom = top + size.height;
912
                            break;
913
                    }                    
914
                }
915
                return (top >= scroll.y && bottom <= scroll.y + page.height);
916
            })) {
917
                // all failed, snap the last position onto the page as best
918
                // we can - can't do anything if the element is higher than the
919
                // space available.
920
                if (bottom > page.height) {
921
                    top = scroll.y + page.height - size.height;
922
                }
923
                if (top < 0) {
924
                    top = 0;
925
                }
926
            }
927
            element.setStyle('top', top);
928
            
929
            /* update the jx layout if necessary */
930
            var jxl = element.retrieve('jxLayout');
931
            if (jxl) {
932
                jxl.options.left = left;
933
                jxl.options.top = top;
934
            }
935
        }
936
});
937

    
938
/**
939
 * Class: Jx.Chrome
940
 * A mix-in class that provides chrome helper functions.  Chrome is the
941
 * extraneous visual element that provides the look and feel to some elements
942
 * i.e. dialogs.  Chrome is added inside the element specified but may
943
 * bleed outside the element to provide drop shadows etc.  This is done by
944
 * absolutely positioning the chrome objects in the container based on
945
 * calculations using the margins, borders, and padding of the jxChrome
946
 * class and the element it is added to.
947
 *
948
 * Chrome can consist of either pure CSS border and background colors, or
949
 * a background-image on the jxChrome class.  Using a background-image on
950
 * the jxChrome class creates four images inside the chrome container that
951
 * are positioned in the top-left, top-right, bottom-left and bottom-right
952
 * corners of the chrome container and are sized to fill 50% of the width
953
 * and height.  The images are positioned and clipped such that the 
954
 * appropriate corners of the chrome image are displayed in those locations.
955
 */
956
Jx.Chrome = new Class({
957
    /**
958
     * Property: chrome
959
     * the DOM element that contains the chrome
960
     */
961
    chrome: null,
962
    
963
    /**
964
     * Method: makeChrome
965
     * create chrome on an element.
966
     *
967
     * Parameters:
968
     * element - {HTMLElement} the element to put the chrome on.
969
     */
970
    makeChrome: function(element) {
971
        var c = new Element('div', {
972
            'class':'jxChrome',
973
            events: {
974
                contextmenu: function(e) { e.stop(); }
975
            }      
976
        });
977
        
978
        /* add to element so we can get the background image style */
979
        element.adopt(c);
980
        
981
        /* pick up any offset because of chrome, set
982
         * through padding on the chrome object.  Other code can then
983
         * make use of these offset values to fix positioning.
984
         */
985
        this.chromeOffsets = c.getPaddingSize();
986
        c.setStyle('padding', 0);
987
        
988
        /* get the chrome image from the background image of the element */
989
        /* the app: protocol check is for adobe air support */
990
        var src = c.getStyle('backgroundImage');
991
        if (!(src.contains('http://') || src.contains('https://') || src.contains('file://') || src.contains('app:/'))) {
992
            src = null;
993
        } else {
994
            src = src.slice(4,-1);
995
            /* this only seems to be IE and Opera, but they add quotes
996
             * around the url - yuck
997
             */
998
            if (src.charAt(0) == '"') {
999
                src = src.slice(1,-1);
1000
            }
1001

    
1002
            /* and remove the background image */
1003
            c.setStyle('backgroundImage', 'none');
1004

    
1005
            /* make chrome */
1006
            ['TR','TL','BL','BR'].each(function(s){
1007
                c.adopt(
1008
                    new Element('div',{
1009
                        'class':'jxChrome'+s
1010
                    }).adopt(
1011
                    new Element('img',{
1012
                        'class':'png24',
1013
                        src:src,
1014
                        alt: '',
1015
                        title: ''
1016
                    })));
1017
            }, this);
1018
        }
1019
        if (!window.opera) {
1020
            c.adopt(Jx.createIframeShim());
1021
        }
1022
        
1023
        /* remove from DOM so the other resizing logic works as expected */
1024
        c.dispose();    
1025
        this.chrome = c;
1026
    },
1027
    /**
1028
     * Method: showChrome
1029
     * show the chrome on an element.  This creates the chrome if necessary.
1030
     * If the chrome has been previously created and not removed, you can
1031
     * call this without an element and it will just resize the chrome within
1032
     * its existing element.  You can also pass in a different element from
1033
     * which the chrome was previously attached to and it will move the chrome
1034
     * to the new element.
1035
     *
1036
     * Parameters:
1037
     * element - {HTMLElement} the element to show the chrome on.
1038
     */
1039
    showChrome: function(element) {
1040
        element = $(element);
1041
        if (!this.chrome) {
1042
            this.makeChrome(element);
1043
        }
1044
        this.resizeChrome(element);
1045
        if (element && this.chrome.parentNode !== element) {
1046
            element.adopt(this.chrome);
1047
        }
1048
    },
1049
    /**
1050
     * Method: hideChrome
1051
     * removes the chrome from the DOM.  If you do this, you can't
1052
     * call showChrome with no arguments.
1053
     */
1054
    hideChrome: function() {
1055
        if (this.chrome) {
1056
            this.chrome.dispose();
1057
        }
1058
    },
1059
    resizeChrome: function(o) {
1060
        if (this.chrome && Browser.Engine.trident4) {
1061
            this.chrome.setContentBoxSize($(o).getBorderBoxSize());
1062
        }
1063
    }
1064
});
1065

    
1066
/**
1067
 * Class: Jx.Addable
1068
 * A mix-in class that provides a helper function that allows an object
1069
 * to be added to an existing element on the page.
1070
 */
1071
Jx.Addable = new Class({
1072
    addable: null,
1073
    /**
1074
     * Method: addTo
1075
     * adds the object to the DOM relative to another element.  If you use
1076
     * 'top' or 'bottom' then the element is added to the relative
1077
     * element (becomes a child node).  If you use 'before' or 'after'
1078
     * then the element is inserted adjacent to the reference node. 
1079
     *
1080
     * Parameters:
1081
     * reference - {Object} the DOM element or id of a DOM element
1082
     * to append the object relative to
1083
     * where - {String} where to append the element in relation to the
1084
     * reference node.  Can be 'top', 'bottom', 'before' or 'after'.
1085
     * The default is 'bottom'.
1086
     *
1087
     * Returns:
1088
     * the object itself, which is useful for chaining calls together
1089
     */
1090
    addTo: function(reference, where) {
1091
        $(this.addable || this.domObj).inject(reference,where);
1092
        this.fireEvent('addTo',this);
1093
        return this;
1094
    },
1095
    
1096
    toElement: function() {
1097
        return this.addable || this.domObj;
1098
    }
1099
});// $Id: button.js 423 2009-05-12 12:37:56Z pagameba $
1100
/**
1101
 * Class: Jx.Button
1102
 *
1103
 * Extends: Object
1104
 *
1105
 * Implements: Options, Events, <Jx.Addable>
1106
 *
1107
 * Jx.Button creates a clickable element that can be added to a web page.
1108
 * When the button is clicked, it fires a 'click' event.
1109
 *
1110
 * The CSS styling for a button is controlled by several classes related
1111
 * to the various objects in the button's HTML structure:
1112
 *
1113
 * (code)
1114
 * <div class="jxButtonContainer">
1115
 *  <a class="jxButton">
1116
 *   <span class="jxButtonContent">
1117
 *    <img class="jxButtonIcon" src="image_url">
1118
 *    <span class="jxButtonLabel">button label</span>
1119
 *   </span>
1120
 *  </a>
1121
 * </div>
1122
 * (end)
1123
 *
1124
 * The CSS classes will change depending on the type option passed to the
1125
 * constructor of the button.  The default type is Button.  Passing another
1126
 * value such as Tab will cause all the CSS classes to change from jxButton
1127
 * to jxTab.  For example:
1128
 *
1129
 * (code)
1130
 * <div class="jxTabContainer">
1131
 *  <a class="jxTab">
1132
 *   <span class="jxTabContent">
1133
 *    <img class="jxTabIcon" src="image_url">
1134
 *    <span class="jxTabLabel">tab label</span>
1135
 *   </span>
1136
 *  </a>
1137
 * </div>
1138
 * (end)
1139
 *
1140
 * When you construct a new instance of Jx.Button, the button does not
1141
 * automatically get inserted into the web page.  Typically a button
1142
 * is used as part of building another capability such as a Jx.Toolbar.
1143
 * However, if you want to manually insert the button into your application,
1144
 * you may use the addTo method to append or insert the button into the 
1145
 * page.  
1146
 *
1147
 * There are two modes for a button, normal and toggle.  A toggle button
1148
 * has an active state analogous to a checkbox.  A toggle button generates
1149
 * different events (down and up) from a normal button (click).  To create
1150
 * a toggle button, pass toggle: true to the Jx.Button constructor.
1151
 *
1152
 * To use a Jx.Button in an application, you should to register for the
1153
 * 'click' event.  You can pass a function in the 'onClick' option when
1154
 * constructing a button or you can call the addEvent('click', myFunction)
1155
 * method.  The addEvent method can be called several times, allowing more
1156
 * than one function to be called when a button is clicked.  You can use the 
1157
 * removeEvent('click', myFunction) method to stop receiving click events.
1158
 *
1159
 * Example:
1160
 *
1161
 * (code)
1162
 * var button = new Jx.Button(options);
1163
 * button.addTo('myListItem'); // the id of an LI in the page.
1164
 * (end)
1165
 *
1166
 * (code)
1167
 * Example:
1168
 * var options = {
1169
 *     imgPath: 'images/mybutton.png',
1170
 *     tooltip: 'click me!',
1171
 *     label: 'click me',
1172
 *     onClick: function() {
1173
 *         alert('you clicked me');
1174
 *     }
1175
 * };
1176
 * var button = new Jx.Button(options);
1177
 * button.addEvent('click', anotherFunction);
1178
 *
1179
 * function anotherFunction() {
1180
 *   alert('a second alert for a single click');
1181
 * }
1182
 * (end)
1183
 *
1184
 * Events:
1185
 * click - the button was pressed and released (only if type is not 'toggle').
1186
 * down - the button is down (only if type is 'toggle')
1187
 * up - the button is up (only if the type is 'toggle').
1188
 *
1189
 * License: 
1190
 * Copyright (c) 2008, DM Solutions Group Inc.
1191
 * 
1192
 * This file is licensed under an MIT style license
1193
 */
1194
Jx.Button = new Class({
1195
    Family: 'Jx.Button',
1196
    Implements: [Options,Events,Jx.Addable],
1197
    
1198
    /**
1199
     * the HTML element that is inserted into the DOM for this button.  You
1200
     * may reference this object to append it to the DOM or remove it from
1201
     * the DOM if necessary.
1202
     */
1203
    domObj: null,
1204
    
1205
    options: {
1206
        /* Option: id
1207
         * optional.  A string value to use as the ID of the button
1208
         * container.
1209
         */
1210
        id: '',
1211
        /* Option: type
1212
         * optional.  A string value that indicates what type of button this
1213
         * is.  The default value is Button.  The type is used to form the CSS
1214
         * class names used for various HTML elements within the button.
1215
         */
1216
        type: 'Button',
1217
        /* Option: image
1218
         * optional.  A string value that is the url to load the image to
1219
         * display in this button.  The default styles size this image to 16 x
1220
         * 16.  If not provided, then the button will have no icon.
1221
         */
1222
        image: '',
1223
        /* Option: tooltip
1224
         * optional.  A string value to use as the alt/title attribute of the
1225
         * <A> tag that wraps the button, resulting in a tooltip that appears
1226
         * when the user hovers the mouse over a button in most browsers.  If
1227
         * not provided, the button will have no tooltip.
1228
         */
1229
        tooltip: '',
1230
        /* Option: label
1231
         * optional, default is no label.  A string value that is used as a
1232
         * label on the button.
1233
         */
1234
        label: '',
1235
        /* Option: toggle
1236
         * default true, whether the button is a toggle button or not.
1237
         */
1238
        toggle: false,
1239
        /* Option: toggleClass
1240
         * defaults to Toggle, this is class is added to buttons with the
1241
         * option toggle: true
1242
         */
1243
        toggleClass: 'Toggle',
1244
        /* Option: halign
1245
         * horizontal alignment of the button label, 'center' by default. 
1246
         * Other values are 'left' and 'right'.
1247
         */
1248
        halign: 'center',
1249
        /* Option: valign
1250
         * {String} vertical alignment of the button label, 'middle' by
1251
         * default.  Other values are 'top' and 'bottom'.
1252
         */
1253
        valign: 'middle',
1254
        /* Option: active
1255
         * optional, default false.  Controls the initial state of toggle
1256
         * buttons.
1257
         */
1258
        active: false,
1259
        /* Option: enabled
1260
         * whether the button is enabled or not.
1261
         */
1262
        enabled: true,
1263
        /* Option: container
1264
         * the tag name of the HTML element that should be created to contain
1265
         * the button, by default this is 'div'.
1266
         */
1267
        container: 'div'
1268
    },
1269
    /**
1270
     * Constructor: Jx.Button
1271
     * create a new button.
1272
     *
1273
     * Parameters:
1274
     * options - {Object} an object containing optional properties for this
1275
     * button as below.
1276
     */
1277
    initialize : function( options ) {
1278
        this.setOptions(options);
1279
        
1280
        // the main container for the button
1281
        var d = new Element(this.options.container, {'class': 'jx'+this.options.type+'Container'});
1282
        if (this.options.toggle && this.options.toggleClass) {
1283
            d.addClass('jx'+this.options.type+this.options.toggleClass);
1284
        }
1285
        // the clickable part of the button
1286
        var hasFocus;
1287
        var mouseDown;
1288
        var a = new Element('a', {
1289
            'class': 'jx'+this.options.type, 
1290
            href: 'javascript:void(0)', 
1291
            title: this.options.tooltip, 
1292
            alt: this.options.tooltip,
1293
            events: {
1294
                click: this.clicked.bindWithEvent(this),
1295
                drag: (function(e) {e.stop();}).bindWithEvent(this),
1296
                mousedown: (function(e) {
1297
                    this.domA.addClass('jx'+this.options.type+'Pressed');
1298
                    hasFocus = true;
1299
                    mouseDown = true;
1300
                    this.focus();
1301
                }).bindWithEvent(this),
1302
                mouseup: (function(e) {
1303
                    this.domA.removeClass('jx'+this.options.type+'Pressed');
1304
                    mouseDown = false;
1305
                }).bindWithEvent(this),
1306
                mouseleave: (function(e) {
1307
                    this.domA.removeClass('jx'+this.options.type+'Pressed');
1308
                }).bindWithEvent(this),
1309
                mouseenter: (function(e) {
1310
                    if (hasFocus && mouseDown) {
1311
                        this.domA.addClass('jx'+this.options.type+'Pressed');
1312
                    }
1313
                }).bindWithEvent(this),
1314
                keydown: (function(e) {
1315
                    if (e.key == 'enter') {
1316
                        this.domA.addClass('jx'+this.options.type+'Pressed');
1317
                    }
1318
                }).bindWithEvent(this),
1319
                keyup: (function(e) {
1320
                    if (e.key == 'enter') {
1321
                        this.domA.removeClass('jx'+this.options.type+'Pressed');
1322
                    }
1323
                }).bindWithEvent(this),
1324
                blur: function() { hasFocus = false; }
1325
            }
1326
        });
1327
        d.adopt(a);
1328
        
1329
        if (typeof Drag != 'undefined') {
1330
            new Drag(a, {
1331
                onStart: function() {this.stop();}
1332
            });
1333
        }
1334
        
1335
        var s = new Element('span', {'class': 'jx'+this.options.type+'Content'});
1336
        a.adopt(s);
1337
        
1338
        if (this.options.image || !this.options.label) {
1339
            var i = new Element('img', {
1340
                'class':'jx'+this.options.type+'Icon',
1341
                'src': Jx.aPixel.src,
1342
                title: this.options.tooltip, 
1343
                alt: this.options.tooltip
1344
            });
1345
            //if image is not a_pixel, set the background image of the image
1346
            //otherwise let the default css take over.
1347
            if (this.options.image && this.options.image.indexOf('a_pixel.png') == -1) {
1348
                i.setStyle('backgroundImage',"url("+this.options.image+")");
1349
            }
1350
            s.appendChild(i);
1351
            if (this.options.imageClass) {
1352
                i.addClass(this.options.imageClass);
1353
            }
1354
            this.domImg = i;
1355
        }
1356
        
1357
        var l = new Element('span', {
1358
            html: this.options.label
1359
        });
1360
        if (this.options.label) {
1361
            l.addClass('jx'+this.options.type+'Label');
1362
        }
1363
        s.appendChild(l);
1364
        
1365
        if (this.options.id) {
1366
            d.id = this.options.id;
1367
        }
1368
        if (this.options.halign == 'left') {
1369
            d.addClass('jx'+this.options.type+'ContentLeft');                
1370
        }
1371

    
1372
        if (this.options.valign == 'top') {
1373
            d.addClass('jx'+this.options.type+'ContentTop');
1374
        }
1375
        
1376
        this.domA = a;
1377
        this.domLabel = l;
1378
        this.domObj = d;        
1379

    
1380
        //update the enabled state
1381
        this.setEnabled(this.options.enabled);
1382
        
1383
        //update the active state if necessary
1384
        if (this.options.active) {
1385
            this.options.active = false;
1386
            this.setActive(true);
1387
        }
1388
        
1389
    },
1390
    /**
1391
     * Method: clicked
1392
     * triggered when the user clicks the button, processes the
1393
     * actionPerformed event
1394
     *
1395
     * Parameters:
1396
     * evt - {Event} the user click event
1397
     */
1398
    clicked : function(evt) {
1399
        if (this.options.enabled) {
1400
            if (this.options.toggle) {
1401
                this.setActive(!this.options.active);
1402
            } else {
1403
                this.fireEvent('click', {obj: this, event: evt});
1404
            }
1405
        }
1406
        //return false;
1407
    },
1408
    /**
1409
     * Method: isEnabled
1410
     * This returns true if the button is enabled, false otherwise
1411
     *
1412
     * Returns:
1413
     * {Boolean} whether the button is enabled or not
1414
     */
1415
    isEnabled: function() { 
1416
        return this.options.enabled; 
1417
    },
1418
    
1419
    /**
1420
     * Method: setEnabled
1421
     * enable or disable the button.
1422
     *
1423
     * Parameters:
1424
     * enabled - {Boolean} the new enabled state of the button
1425
     */
1426
    setEnabled: function(enabled) {
1427
        this.options.enabled = enabled;
1428
        if (this.options.enabled) {
1429
            this.domObj.removeClass('jxDisabled');
1430
        } else {
1431
            this.domObj.addClass('jxDisabled');
1432
        }
1433
    },
1434
    /**
1435
     * Method: isActive
1436
     * For toggle buttons, this returns true if the toggle button is
1437
     * currently active and false otherwise.
1438
     *
1439
     * Returns:
1440
     * {Boolean} the active state of a toggle button
1441
     */
1442
    isActive: function() { 
1443
        return this.options.active; 
1444
    },
1445
    /**
1446
     * Method: setActive
1447
     * Set the active state of the button
1448
     *
1449
     * Parameters:
1450
     * active - {Boolean} the new active state of the button
1451
     */
1452
    setActive: function(active) {
1453
        if (this.options.active == active) {
1454
            return;
1455
        }
1456
        this.options.active = active;
1457
        if (this.options.active) {
1458
            this.domA.addClass('jx'+this.options.type+'Active');
1459
            this.fireEvent('down', this);
1460
        } else {
1461
            this.domA.removeClass('jx'+this.options.type+'Active');
1462
            this.fireEvent('up', this);
1463
        }
1464
    },
1465
    /**
1466
     * Method: setImage
1467
     * set the image of this button to a new image URL
1468
     *
1469
     * Parameters:
1470
     * path - {String} the new url to use as the image for this button
1471
     */
1472
    setImage: function(path) {
1473
        this.options.image = path;
1474
        if (path) {
1475
            if (!this.domImg) {
1476
                var i = new Element('img', {
1477
                    'class':'jx'+this.options.type+'Icon',
1478
                    'src': Jx.aPixel.src,
1479
                    alt: '',
1480
                    title: ''
1481
                });
1482
                if (this.options.imageClass) {
1483
                    i.addClass(this.options.imageClass);
1484
                }
1485
                this.domA.firstChild.grab(i, 'top');
1486
                this.domImg = i;
1487
            }
1488
            this.domImg.setStyle('backgroundImage',"url("+this.options.image+")");                        
1489
        } else if (this.domImg){
1490
            this.domImg.dispose();
1491
            this.domImg = null;
1492
        }
1493
    },
1494
    /**
1495
     * Method: setLabel
1496
     * 
1497
     * sets the text of the button.  Only works if a label was supplied
1498
     * when the button was constructed
1499
     *
1500
     * Parameters: 
1501
     *
1502
     * label - {String} the new label for the button
1503
     */
1504
    setLabel: function(label) {
1505
        this.domLabel.set('html', label);
1506
        if (!label && this.domLabel.hasClass('jxButtonLabel')) {
1507
            this.domLabel.removeClass('jxButtonLabel');
1508
        } else if (label && !this.domLabel.hasClass('jxButtonLabel')) {
1509
            this.domLabel.addClass('jxButtonLabel');
1510
        }
1511
    },
1512
    /**
1513
     * Method: getLabel
1514
     * 
1515
     * returns the text of the button.
1516
     */
1517
    getLabel: function() {
1518
        return this.domLabel ? this.domLabel.innerHTML : '';
1519
    },
1520
    /**
1521
     * Method: setTooltip
1522
     * sets the tooltip displayed by the button
1523
     *
1524
     * Parameters: 
1525
     * tooltip - {String} the new tooltip
1526
     */
1527
    setTooltip: function(tooltip) {
1528
        if (this.domA) {
1529
            this.domA.set({
1530
                'title':tooltip,
1531
                'alt':tooltip
1532
            });
1533
        }
1534
    },
1535
    /**
1536
     * Method: focus
1537
     * capture the keyboard focus on this button
1538
     */
1539
    focus: function() {
1540
        this.domA.focus();
1541
    },
1542
    /**
1543
     * Method: blur
1544
     * remove the keyboard focus from this button
1545
     */
1546
    blur: function() {
1547
        this.domA.blur();
1548
    }
1549
});
1550
// $Id: flyout.js 423 2009-05-12 12:37:56Z pagameba $
1551
/**
1552
 * Class: Jx.Button.Flyout
1553
 *
1554
 * Extends: <Jx.Button>
1555
 *
1556
 * Implements: <Jx.ContentLoader>, <Jx.AutoPosition>, <Jx.Chrome>
1557
 *
1558
 * Flyout buttons expose a panel when the user clicks the button.  The
1559
 * panel can have arbitrary content.  You must provide any necessary 
1560
 * code to hook up elements in the panel to your application.
1561
 *
1562
 * When the panel is opened, the 'open' event is fired.  When the panel is
1563
 * closed, the 'close' event is fired.  You can register functions to handle
1564
 * these events in the options passed to the constructor (onOpen, onClose).
1565
 * 
1566
 * The user can close the flyout panel by clicking the button again, by
1567
 * clicking anywhere outside the panel and other buttons, or by pressing the
1568
 * 'esc' key.
1569
 *
1570
 * Flyout buttons implement <Jx.ContentLoader> which provides the hooks to
1571
 * insert content into the Flyout element.  Note that the Flyout element
1572
 * is not appended to the DOM until the first time it is opened, and it is
1573
 * removed from the DOM when closed.
1574
 *
1575
 * It is generally best to specify a width and height for your flyout content
1576
 * area through CSS to ensure that it works correctly across all browsers.
1577
 * You can do this for all flyouts using the .jxFlyout CSS selector, or you
1578
 * can apply specific styles to your content elements.
1579
 *
1580
 * A flyout closes other flyouts when it is opened.  It is possible to embed
1581
 * flyout buttons inside the content area of another flyout button.  In this
1582
 * case, opening the inner flyout will not close the outer flyout but it will
1583
 * close any other flyouts that are siblings.
1584
 *
1585
 * Example:
1586
 * (code)
1587
 * var flyout = new Jx.Button.Flyout({
1588
 *      label: 'flyout',
1589
 *      content: 'flyoutContent',
1590
 *      onOpen: function(flyout) {
1591
 *          console.log('flyout opened');
1592
 *      },
1593
 *      onClose: function(flyout) {
1594
 *          console.log('flyout closed');
1595
 *      }
1596
 * });
1597
 * (end)
1598
 *
1599
 * Events:
1600
 * open - this event is triggered when the flyout is opened.
1601
 * close - this event is triggered when the flyout is closed.
1602
 *
1603
 * License: 
1604
 * Copyright (c) 2008, DM Solutions Group Inc.
1605
 * 
1606
 * This file is licensed under an MIT style license
1607
 */
1608
Jx.Button.Flyout = new Class({
1609
    Family: 'Jx.Button.Flyout',
1610
    Extends: Jx.Button,
1611
    Implements: [Jx.ContentLoader, Jx.AutoPosition, Jx.Chrome],
1612
    
1613
    /**
1614
     * Property: content
1615
     * the HTML element that contains the flyout content
1616
     */
1617
    content: null,
1618
    /**
1619
     * Constructor: initialize
1620
     * construct a new instance of a flyout button.  The single options
1621
     * argument takes a combination of options that apply to <Jx.Button>,
1622
     * <Jx.ContentLoader>, and <Jx.AutoPosition>.
1623
     *
1624
     * Parameters: 
1625
     * options - an options object used to initialize the button, see 
1626
     * <Jx.Button.Options>, <Jx.ContentLoader.Options>, and
1627
     * <Jx.AutoPosition.Options> for details.
1628
     */
1629
    initialize: function(options) {
1630
        if (!Jx.Button.Flyout.Stack) {
1631
            Jx.Button.Flyout.Stack = [];
1632
        }
1633
        this.parent(options);
1634
        this.domA.addClass('jx'+this.options.type+'Flyout');
1635
        
1636
        this.contentContainer = new Element('div',{
1637
            'class':'jxFlyout'
1638
        });
1639
        
1640
        this.content = new Element('div', {
1641
            'class': 'jxFlyoutContent'
1642
        });
1643
        if (this.options.contentClass) {
1644
            this.content.addClass(this.options.contentClass);
1645
        }
1646
        this.contentContainer.adopt(this.content);
1647
        
1648
        this.content.store('jxFlyout', this);
1649
        this.loadContent(this.content);
1650
        this.keypressWatcher = this.keypressHandler.bindWithEvent(this);
1651
        this.hideWatcher = this.clickHandler.bindWithEvent(this);
1652
    },
1653
    /**
1654
     * Method: clicked
1655
     * Override <Jx.Button::clicked> to hide/show the content area of the
1656
     * flyout.
1657
     *
1658
     * Parameters:
1659
     * e - {Event} the user event
1660
     */ 
1661
    clicked: function(e) {
1662
        if (!this.options.enabled) {
1663
            return;
1664
        }
1665
        /* find out what we are contained by if we don't already know */
1666
        if (!this.owner) {
1667
            this.owner = document.body;
1668
            var node = $(this.domObj.parentNode);
1669
            while (node != document.body && this.owner == document.body) {
1670
                var flyout = node.retrieve('jxFlyout');
1671
                if (flyout) {
1672
                    this.owner = flyout;
1673
                    break;
1674
                } else {
1675
                    node = $(node.parentNode);
1676
                }
1677
            }
1678
        }
1679
        if (Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1] == this) {
1680
            this.hide();
1681
            return;
1682
        } else if (this.owner != document.body) {
1683
            /* if we are part of another flyout, close any open flyouts
1684
             * inside the parent and register this as the current flyout
1685
             */
1686
            if (this.owner.currentFlyout == this) {
1687
                /* if the flyout to close is this flyout,
1688
                 * hide this and return */
1689
                this.hide();
1690
                return;
1691
            } else if (this.owner.currentFlyout) {
1692
                this.owner.currentFlyout.hide();
1693
            }
1694
            this.owner.currentFlyout = this;                
1695
        } else {
1696
            /* if we are at the top level, close the entire stack before
1697
             * we open
1698
             */
1699
            while (Jx.Button.Flyout.Stack.length) {
1700
                Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1].hide();
1701
            }
1702
        }
1703
        // now we go on the stack.
1704
        Jx.Button.Flyout.Stack.push(this);
1705

    
1706
        this.options.active = true;
1707
        this.domA.addClass('jx'+this.options.type+'Active');
1708
        this.contentContainer.setStyle('visibility','hidden');
1709
        $(document.body).adopt(this.contentContainer);
1710
        this.content.getChildren().each(function(child) {
1711
            if (child.resize) { 
1712
                child.resize(); 
1713
            }
1714
        });
1715
        this.showChrome(this.contentContainer);
1716
        
1717
        this.position(this.contentContainer, this.domObj, {
1718
            horizontal: ['left left', 'right right'],
1719
            vertical: ['bottom top', 'top bottom'],
1720
            offsets: this.chromeOffsets
1721
        });
1722
        
1723
        /* we have to size the container for IE to render the chrome correctly
1724
         * there is some horrible peekaboo bug in IE 6
1725
         */
1726
        this.contentContainer.setContentBoxSize($(this.content).getMarginBoxSize());
1727
        
1728
        this.contentContainer.setStyle('visibility','');
1729

    
1730
        document.addEvent('keydown', this.keypressWatcher);
1731
        document.addEvent('click', this.hideWatcher);
1732
        this.fireEvent('open', this);
1733
    },
1734
    /**
1735
     * Method: hide
1736
     * Closes the flyout if open
1737
     */
1738
    hide: function() {
1739
        if (this.owner != document.body) {
1740
            this.owner.currentFlyout = null;            
1741
        }
1742
        Jx.Button.Flyout.Stack.pop();
1743
        this.setActive(false);
1744
        this.contentContainer.dispose();
1745
        document.removeEvent('keydown', this.keypressWatcher);    
1746
        document.removeEvent('click', this.hideWatcher);
1747
        this.fireEvent('close', this);
1748
    },
1749
    /* hide flyout if the user clicks outside of the flyout */
1750
    clickHandler: function(e) {
1751
        e = new Event(e);
1752
        var elm = $(e.target);
1753
        var flyout = Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1];
1754
        if (!elm.descendantOf(flyout.content) &&
1755
            !elm.descendantOf(flyout.domObj)) {
1756
            flyout.hide();
1757
        }
1758
    },
1759
    /* hide flyout if the user presses the ESC key */
1760
    keypressHandler: function(e) {
1761
        e = new Event(e);
1762
        if (e.key == 'esc') {
1763
            Jx.Button.Flyout.Stack[Jx.Button.Flyout.Stack.length - 1].hide();
1764
        }
1765
    }
1766
});// $Id: layout.js 423 2009-05-12 12:37:56Z pagameba $
1767
/**
1768
 * Class: Jx.Layout
1769
 *
1770
 * Extends: Object
1771
 * 
1772
 * Implements: Options, Events
1773
 *
1774
 * Jx.Layout is used to provide more flexible layout options for applications
1775
 *
1776
 * Jx.Layout wraps an existing DOM element (typically a div) and provides
1777
 * extra functionality for sizing that element within its parent and sizing
1778
 * elements contained within it that have a 'resize' function attached to them.
1779
 *
1780
 * To create a Jx.Layout, pass the element or id plus an options object to
1781
 * the constructor.
1782
 *
1783
 * Example:
1784
 * (code)
1785
 * var myContainer = new Jx.Layout('myDiv', options);
1786
 * (end)
1787
 *
1788
 * Events:
1789
 * sizeChange - fired when the size of the container changes
1790
 *
1791
 * License: 
1792
 * Copyright (c) 2008, DM Solutions Group Inc.
1793
 * 
1794
 * This file is licensed under an MIT style license
1795
 */
1796
 
1797
Jx.Layout = new Class({
1798
    Family: 'Jx.Layout',
1799
    Implements: [Options,Events],
1800
    
1801
    options: {
1802
        /* Option: propagate
1803
         * boolean, controls propogation of resize to child nodes.
1804
         * True by default. If set to false, changes in size will not be
1805
         * propogated to child nodes.
1806
         */
1807
        propagate: true,
1808
        /* Option: position
1809
         * how to position the element, either 'absolute' or 'relative'.
1810
         * The default (if not passed) is 'absolute'.  When using
1811
         * 'absolute' positioning, both the width and height are
1812
         * controlled by Jx.Layout.  If 'relative' positioning is used
1813
         * then only the width is controlled, allowing the height to
1814
         * be controlled by its content.
1815
         */
1816
        position: 'absolute',
1817
        /* Option: left
1818
         * the distance (in pixels) to maintain the left edge of the element
1819
         * from its parent element.  The default value is 0.  If this is set
1820
         * to 'null', then the left edge can be any distance from its parent
1821
         * based on other parameters.
1822
         */
1823
        left: 0,
1824
        /* Option: right
1825
         * the distance (in pixels) to maintain the right edge of the element
1826
         * from its parent element.  The default value is 0.  If this is set
1827
         * to 'null', then the right edge can be any distance from its parent
1828
         * based on other parameters.
1829
         */
1830
        right: 0,
1831
        /* Option: top
1832
         * the distance (in pixels) to maintain the top edge of the element
1833
         * from its parent element.  The default value is 0.  If this is set
1834
         * to 'null', then the top edge can be any distance from its parent
1835
         * based on other parameters.
1836
         */
1837
        top: 0,
1838
        /* Option: bottom
1839
         * the distance (in pixels) to maintain the bottom edge of the element
1840
         * from its parent element.  The default value is 0.  If this is set
1841
         * to 'null', then the bottom edge can be any distance from its parent
1842
         * based on other parameters.
1843
         */
1844
        bottom: 0,
1845
        /* Option: width
1846
         * the width (in pixels) of the element.  The default value is null.
1847
         * If this is set to 'null', then the width can be any value based on
1848
         * other parameters.
1849
         */
1850
        width: null,
1851
        /* Option: height
1852
         * the height (in pixels) of the element.  The default value is null.
1853
         * If this is set to 'null', then the height can be any value based on
1854
         * other parameters.
1855
         */
1856
        height: null,
1857
        /* Option: minWidth
1858
         * the minimum width that the element can be sized to.  The default
1859
         * value is 0.
1860
         */
1861
        minWidth: 0,
1862
        /* Option: minHeight
1863
         * the minimum height that the element can be sized to.  The
1864
         * default value is 0.
1865
         */
1866
        minHeight: 0,
1867
        /* Option: maxWidth
1868
         * the maximum width that the element can be sized to.  The default
1869
         * value is -1, which means no maximum.
1870
         */
1871
        maxWidth: -1,
1872
        /* Option: maxHeight
1873
         * the maximum height that the element can be sized to.  The
1874
         * default value is -1, which means no maximum.
1875
         */
1876
        maxHeight: -1
1877
    },
1878
    /**
1879
     * Constructor: Jx.Layout
1880
     * Create a new instance of Jx.Layout.
1881
     *
1882
     * Parameters:
1883
     * domObj - {HTMLElement} element or id to apply the layout to
1884
     * options - <Jx.Layout.Options>
1885
     */
1886
    initialize: function(domObj, options) {
1887
        this.setOptions(options);
1888
        this.domObj = $(domObj);
1889
        this.domObj.resize = this.resize.bind(this);
1890
        this.domObj.setStyle('position', this.options.position);
1891
        this.domObj.store('jxLayout', this);
1892

    
1893
        if (document.body == this.domObj.parentNode) {
1894
            window.addEvent('resize', this.windowResize.bindWithEvent(this));
1895
            window.addEvent('load', this.windowResize.bind(this));                
1896
        }
1897
        //this.resize();
1898
    },
1899
    
1900
    /**
1901
     * Method: windowResize
1902
     * when the window is resized, any Jx.Layout controlled elements that are
1903
     * direct children of the BODY element are resized
1904
     */
1905
     windowResize: function() {
1906
         this.resize();
1907
         if (this.resizeTimer) {
1908
             $clear(this.resizeTimer);
1909
             this.resizeTimer = null;
1910
         }
1911
         this.resizeTimer = this.resize.delay(50, this);
1912
    },
1913
    
1914
    /**
1915
     * Method: resize
1916
     * resize the element controlled by this Jx.Layout object.
1917
     *
1918
     * Parameters:
1919
     * options - new options to apply, see <Jx.Layout.Options>
1920
     */
1921
    resize: function(options) {
1922
         /* this looks like a really big function but actually not
1923
          * much code gets executed in the two big if statements
1924
          */
1925
        this.resizeTimer = null;
1926
        var needsResize = false;
1927
        if (options) {
1928
            for (var i in options) {
1929
                //prevent forceResize: false from causing a resize
1930
                if (i == 'forceResize') {
1931
                    continue;
1932
                }
1933
                if (this.options[i] != options[i]) {
1934
                    needsResize = true;
1935
                    this.options[i] = options[i];
1936
                }
1937
            }
1938
            if (options.forceResize) {
1939
                needsResize = true;
1940
            }
1941
        }
1942
        if (!$(this.domObj.parentNode)) {
1943
            return;
1944
        }
1945
        
1946
        var parentSize;
1947
        if (this.domObj.parentNode.tagName == 'BODY') {
1948
            parentSize = Jx.getPageDimensions();
1949
        } else {
1950
            parentSize = $(this.domObj.parentNode).getContentBoxSize();
1951
        }
1952
    
1953
        if (this.lastParentSize && !needsResize) {
1954
            needsResize = (this.lastParentSize.width != parentSize.width || 
1955
                          this.lastParentSize.height != parentSize.height);
1956
        } else {
1957
            needsResize = true;
1958
        }
1959
        this.lastParentSize = parentSize;            
1960
        
1961
        if (!needsResize) {
1962
            return;
1963
        }
1964
        
1965
        var l, t, w, h;
1966
        
1967
        /* calculate left and width */
1968
        if (this.options.left != null) {
1969
            /* fixed left */
1970
            l = this.options.left;
1971
            if (this.options.right == null) {
1972
                /* variable right */
1973
                if (this.options.width == null) {
1974
                    /* variable right and width
1975
                     * set right to min, stretch width */
1976
                    w = parentSize.width - l;
1977
                    if (w < this.options.minWidth ) {
1978
                        w = this.options.minWidth;
1979
                    }
1980
                    if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
1981
                        w = this.options.maxWidth;
1982
                    }
1983
                } else {
1984
                    /* variable right, fixed width
1985
                     * use width
1986
                     */
1987
                    w = this.options.width;
1988
                }
1989
            } else {
1990
                /* fixed right */
1991
                if (this.options.width == null) {
1992
                    /* fixed right, variable width
1993
                     * stretch width
1994
                     */
1995
                    w = parentSize.width - l - this.options.right;
1996
                    if (w < this.options.minWidth) {
1997
                        w = this.options.minWidth;
1998
                    }
1999
                    if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
2000
                        w = this.options.maxWidth;
2001
                    }
2002
                } else {
2003
                    /* fixed right, fixed width
2004
                     * respect left and width, allow right to stretch
2005
                     */
2006
                    w = this.options.width;
2007
                }
2008
            }
2009
            
2010
        } else {
2011
            if (this.options.right == null) {
2012
                if (this.options.width == null) {
2013
                    /* variable left, width and right
2014
                     * set left, right to min, stretch width
2015
                     */
2016
                     l = 0;
2017
                     w = parentSize.width;
2018
                     if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
2019
                         l = l + parseInt(w - this.options.maxWidth)/2;
2020
                         w = this.options.maxWidth;
2021
                     }
2022
                } else {
2023
                    /* variable left, fixed width, variable right
2024
                     * distribute space between left and right
2025
                     */
2026
                    w = this.options.width;
2027
                    l = parseInt((parentSize.width - w)/2);
2028
                    if (l < 0) {
2029
                        l = 0;
2030
                    }
2031
                }
2032
            } else {
2033
                if (this.options.width != null) {
2034
                    /* variable left, fixed width, fixed right
2035
                     * left is calculated directly
2036
                     */
2037
                    w = this.options.width;
2038
                    l = parentSize.width - w - this.options.right;
2039
                    if (l < 0) {
2040
                        l = 0;
2041
                    }
2042
                } else {
2043
                    /* variable left and width, fixed right
2044
                     * set left to min value and stretch width
2045
                     */
2046
                    l = 0;
2047
                    w = parentSize.width - this.options.right;
2048
                    if (w < this.options.minWidth) {
2049
                        w = this.options.minWidth;
2050
                    }
2051
                    if (this.options.maxWidth >= 0 && w > this.options.maxWidth) {
2052
                        l = w - this.options.maxWidth - this.options.right;
2053
                        w = this.options.maxWidth;                        
2054
                    }
2055
                }
2056
            }
2057
        }
2058
        
2059
        /* calculate the top and height */
2060
        if (this.options.top != null) {
2061
            /* fixed top */
2062
            t = this.options.top;
2063
            if (this.options.bottom == null) {
2064
                /* variable bottom */
2065
                if (this.options.height == null) {
2066
                    /* variable bottom and height
2067
                     * set bottom to min, stretch height */
2068
                    h = parentSize.height - t;
2069
                    if (h < this.options.minHeight) {
2070
                        h = this.options.minHeight;
2071
                    }
2072
                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
2073
                        h = this.options.maxHeight;
2074
                    }
2075
                } else {
2076
                    /* variable bottom, fixed height
2077
                     * stretch height
2078
                     */
2079
                    h = this.options.height;
2080
                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
2081
                        t = h - this.options.maxHeight;
2082
                        h = this.options.maxHeight;
2083
                    }
2084
                }
2085
            } else {
2086
                /* fixed bottom */
2087
                if (this.options.height == null) {
2088
                    /* fixed bottom, variable height
2089
                     * stretch height
2090
                     */
2091
                    h = parentSize.height - t - this.options.bottom;
2092
                    if (h < this.options.minHeight) {
2093
                        h = this.options.minHeight;
2094
                    }
2095
                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
2096
                        h = this.options.maxHeight;
2097
                    }                
2098
                } else {
2099
                    /* fixed bottom, fixed height
2100
                     * respect top and height, allow bottom to stretch
2101
                     */
2102
                    h = this.options.height;
2103
                }
2104
            }
2105
        } else {
2106
            if (this.options.bottom == null) {
2107
                if (this.options.height == null) {
2108
                    /* variable top, height and bottom
2109
                     * set top, bottom to min, stretch height
2110
                     */
2111
                     t = 0;
2112
                     h = parentSize.height;
2113
                     if (h < this.options.minHeight) {
2114
                         h = this.options.minHeight;
2115
                     }
2116
                     if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
2117
                         t = parseInt((parentSize.height - this.options.maxHeight)/2);
2118
                         h = this.options.maxHeight;
2119
                     }
2120
                } else {
2121
                    /* variable top, fixed height, variable bottom
2122
                     * distribute space between top and bottom
2123
                     */
2124
                    h = this.options.height;
2125
                    t = parseInt((parentSize.height - h)/2);
2126
                    if (t < 0) {
2127
                        t = 0;
2128
                    }
2129
                }
2130
            } else {
2131
                if (this.options.height != null) {
2132
                    /* variable top, fixed height, fixed bottom
2133
                     * top is calculated directly
2134
                     */
2135
                    h = this.options.height;
2136
                    t = parentSize.height - h - this.options.bottom;
2137
                    if (t < 0) {
2138
                        t = 0;
2139
                    }
2140
                } else {
2141
                    /* variable top and height, fixed bottom
2142
                     * set top to min value and stretch height
2143
                     */
2144
                    t = 0;
2145
                    h = parentSize.height - this.options.bottom;
2146
                    if (h < this.options.minHeight) {
2147
                        h = this.options.minHeight;
2148
                    }
2149
                    if (this.options.maxHeight >= 0 && h > this.options.maxHeight) {
2150
                        t = parentSize.height - this.options.maxHeight - this.options.bottom;
2151
                        h = this.options.maxHeight;
2152
                    }
2153
                }
2154
            }
2155
        }
2156
        
2157
        //TODO: check left, top, width, height against current styles
2158
        // and only apply changes if they are not the same.
2159
        
2160
        /* apply the new sizes */
2161
        var sizeOpts = {width: w};
2162
        if (this.options.position == 'absolute') {
2163
            var padding = $(this.domObj.parentNode).getPaddingSize();
2164
            this.domObj.setStyles({
2165
                position: this.options.position,
2166
                left: l+padding.left,
2167
                top: t+padding.top
2168
            });
2169
            sizeOpts.height = h;
2170
        } else {
2171
            if (this.options.height) {
2172
                sizeOpts.height = this.options.height;
2173
            }
2174
        }
2175
        this.domObj.setBorderBoxSize(sizeOpts);
2176
        
2177
        if (this.options.propagate) {
2178
            // propogate changes to children
2179
            var o = {forceResize: options ? options.forceResize : false};
2180
            $A(this.domObj.childNodes).each(function(child){
2181
                if (child.resize && child.getStyle('display') != 'none') {
2182
                    child.resize.delay(0,child,o);                
2183
                }
2184
            });
2185
        }
2186

    
2187
        this.fireEvent('sizeChange',this);
2188
    }
2189
});// $Id: tab.js 423 2009-05-12 12:37:56Z pagameba $
2190
/**
2191
 * Class: Jx.Button.Tab
2192
 *
2193
 * Extends: <Jx.Button>
2194
 *
2195
 * Implements: <Jx.ContentLoader>
2196
 *
2197
 * A single tab in a tab set.  A tab has a label (displayed in the tab) and a
2198
 * content area that is displayed when the tab is active.  A tab has to be
2199
 * added to both a <Jx.TabSet> (for the content) and <Jx.Toolbar> (for the
2200
 * actual tab itself) in order to be useful.  Alternately, you can use
2201
 * a <Jx.TabBox> which combines both into a single control at the cost of
2202
 * some flexibility in layout options.
2203
 *
2204
 * A tab is a <Jx.ContentLoader> and you can specify the initial content of
2205
 * the tab using any of the methods supported by 
2206
 * <Jx.ContentLoader::loadContent>.  You can acccess the actual DOM element
2207
 * that contains the content (if you want to dynamically insert content
2208
 * for instance) via the <Jx.Tab::content> property.
2209
 *
2210
 * A tab is a button of type *toggle* which means that it emits the *up*
2211
 * and *down* events.
2212
 *
2213
 * Example:
2214
 * (code)
2215
 * var tab1 = new Jx.Button.Tab({
2216
 *     label: 'tab 1', 
2217
 *     content: 'content1',
2218
 *     onDown: function(tab) {
2219
 *         console.log('tab became active');
2220
 *     },
2221
 *     onUp: function(tab) {
2222
 *         console.log('tab became inactive');
2223
 *     }
2224
 * });
2225
 * (end)
2226
 *
2227
 * 
2228
 *
2229
 * License: 
2230
 * Copyright (c) 2008, DM Solutions Group Inc.
2231
 * 
2232
 * This file is licensed under an MIT style license
2233
 */
2234
Jx.Button.Tab = new Class({
2235
    Family: 'Jx.Button.Tab',
2236
    Extends: Jx.Button,
2237
    Implements: [Jx.ContentLoader],
2238
    /**
2239
     * Property: content
2240
     * {HTMLElement} The content area that is displayed when the tab is active.
2241
     */
2242
    content: null,
2243
    /**
2244
     * Constructor: Jx.Button.Tab
2245
     * Create a new instance of Jx.Button.Tab.  Any layout options passed are used
2246
     * to create a <Jx.Layout> for the tab content area.
2247
     *
2248
     * Parameters:
2249
     * options - {Object} an object containing options that are used
2250
     * to control the appearance of the tab.  See <Jx.Button>,
2251
     * <Jx.ContentLoader::loadContent> and <Jx.Layout::Jx.Layout> for
2252
     * valid options.
2253
     */
2254
    initialize : function( options) {
2255
        this.parent($merge(options, {type:'Tab', toggle:true}));
2256
        this.content = new Element('div', {'class':'tabContent'});
2257
        new Jx.Layout(this.content, options);
2258
        this.loadContent(this.content);
2259
        var that = this;
2260
        this.addEvent('down', function(){that.content.addClass('tabContentActive');});
2261
        this.addEvent('up', function(){that.content.removeClass('tabContentActive');});
2262
        
2263
        if (this.options.close) {
2264
            this.domObj.addClass('jxTabClose');
2265
            var a = new Element('a', {
2266
                'class': 'jxTabClose',
2267
                events: {
2268
                    'click': (function(){
2269
                        this.fireEvent('close');                        
2270
                    }).bind(this)
2271
                } 
2272
            });
2273
            a.adopt(new Element('img', {
2274
                src: Jx.aPixel.src,
2275
                alt: '',
2276
                title: ''
2277
            }));
2278
            this.domObj.adopt(a);
2279
        }
2280
    },
2281
    /**
2282
     * Method: clicked
2283
     * triggered when the user clicks the button, processes the
2284
     * actionPerformed event
2285
     */
2286
    clicked : function(evt) {
2287
        if (this.options.enabled) {
2288
            this.setActive(true);            
2289
        }
2290
    }
2291
});// $Id: colorpalette.js 423 2009-05-12 12:37:56Z pagameba $
2292
/**
2293
 * Class: Jx.ColorPalette
2294
 *
2295
 * Extends: Object
2296
 *
2297
 * Implements: Options, Events, <Jx.Addable>
2298
 *
2299
 * A Jx.ColorPalette presents a user interface for selecting colors.
2300
 * Currently, the user can either enter a HEX colour value or select from a
2301
 * palette of web-safe colours.  The user can also enter an opacity value.
2302
 *
2303
 * A Jx.ColorPalette can be embedded anywhere in a web page using its addTo
2304
 * method.  However, a <Jx.Button> subclass is provided (<Jx.Button.Color>)
2305
 * that embeds a colour panel inside a button for easy use in toolbars.
2306
 *
2307
 * Colour changes are propogated via a change event.  To be notified 
2308
 * of changes in a Jx.ColorPalette, use the addEvent method.
2309
 *
2310
 * Example:
2311
 * (code)
2312
 * (end)
2313
 *
2314
 * Events:
2315
 * change - triggered when the color changes.
2316
 * click - the user clicked on a color swatch (emitted after a change event)
2317
 *
2318
 * License: 
2319
 * Copyright (c) 2008, DM Solutions Group Inc.
2320
 * 
2321
 * This file is licensed under an MIT style license
2322
 */
2323
Jx.ColorPalette = new Class({
2324
    Family: 'Jx.ColorPalette',
2325
    Implements: [Options, Events, Jx.Addable],
2326
    /**
2327
     * Property: {HTMLElement} domObj
2328
     * the HTML element representing the color panel
2329
     */
2330
    domObj: null,
2331
    options: {
2332
        /* Option: parent
2333
         * default null, the DOM element to add the palette to.
2334
         */
2335
        parent: null,
2336
        /* Option: color
2337
         * default #000000, the initially selected color
2338
         */
2339
        color: '#000000',
2340
        /* Option: alpha
2341
         * default 100, the initial alpha value
2342
         */
2343
        alpha: 1,
2344
        /* Option: hexColors
2345
         * an array of hex colors for creating the palette, defaults to a
2346
         * set of web safe colors.
2347
         */
2348
        hexColors: ['00', '33', '66', '99', 'CC', 'FF'],
2349
        /* Option: alphaLabel
2350
         * the text to display next to the alpha input for i18n.
2351
         */
2352
        alphaLabel: 'alpha (%)'
2353
    },
2354
    /**
2355
     * Constructor: Jx.ColorPalette
2356
     * initialize a new instance of Jx.ColorPalette
2357
     *
2358
     * Parameters:
2359
     * options - <Jx.ColorPalette.Options>
2360
     */
2361
    initialize: function(options) {
2362
        this.setOptions(options);
2363

    
2364
        this.domObj = new Element('div', {
2365
            id: this.options.id,
2366
            'class':'jxColorPalette'
2367
        });
2368

    
2369
        var top = new Element('div', {'class':'jxColorBar'});
2370
        var d = new Element('div', {'class':'jxColorPreview'});
2371

    
2372
        this.selectedSwatch = new Element('div', {'class':'jxColorSelected'});
2373
        this.previewSwatch = new Element('div', {'class':'jxColorHover'});
2374
        d.adopt(this.selectedSwatch);
2375
        d.adopt(this.previewSwatch);
2376
        
2377
        top.adopt(d);
2378

    
2379
        this.colorInputLabel = new Element('label', {'class':'jxColorLabel', html:'#'});
2380
        top.adopt(this.colorInputLabel);
2381

    
2382
        var cc = this.changed.bind(this);
2383
        this.colorInput = new Element('input', {
2384
            'class':'jxHexInput',
2385
            'type':'text',
2386
            'maxLength':6,
2387
            events: {
2388
                'keyup':cc,
2389
                'blur':cc,
2390
                'change':cc
2391
            }
2392
        });
2393

    
2394
        top.adopt(this.colorInput);
2395

    
2396
        this.alphaLabel = new Element('label', {'class':'jxAlphaLabel', 'html':this.options.alphaLabel});
2397
        top.adopt(this.alphaLabel);
2398

    
2399
        this.alphaInput = new Element('input', {
2400
            'class':'jxAlphaInput',
2401
            'type':'text',
2402
            'maxLength':3,
2403
            events: {
2404
                'keyup': this.alphaChanged.bind(this)
2405
            }
2406
        });
2407
        top.adopt(this.alphaInput);
2408

    
2409
        this.domObj.adopt(top);
2410

    
2411
        var swatchClick = this.swatchClick.bindWithEvent(this);
2412
        var swatchOver = this.swatchOver.bindWithEvent(this);
2413

    
2414
        var table = new Element('table', {'class':'jxColorGrid'});
2415
        var tbody = new Element('tbody');
2416
        table.adopt(tbody);
2417
        for (var i=0; i<12; i++) {
2418
            var tr = new Element('tr');
2419
            for (var j=-3; j<18; j++) {
2420
                var bSkip = false;
2421
                var r, g, b;
2422
                /* hacky approach to building first three columns
2423
                 * because I couldn't find a good way to do it
2424
                 * programmatically
2425
                 */
2426

    
2427
                if (j < 0) {
2428
                    if (j == -3 || j == -1) {
2429
                        r = g = b = 0;
2430
                        bSkip = true;
2431
                    } else {
2432
                        if (i<6) {
2433
                            r = g = b = i;
2434
                        } else {
2435
                            if (i == 6) {
2436
                                r = 5; g = 0; b = 0;
2437
                            } else if (i == 7) {
2438
                                r = 0; g = 5; b = 0;
2439
                            } else if (i == 8) {
2440
                                r = 0; g = 0; b = 5;
2441
                            } else if (i == 9) {
2442
                                r = 5; g = 5; b = 0;
2443
                            } else if (i == 10) {
2444
                                r = 0; g = 5; b = 5;
2445
                            } else if (i == 11) {
2446
                                r = 5; g = 0; b = 5;
2447
                            }
2448
                        }
2449
                    }
2450
                } else {
2451
                    /* remainder of the columns are built
2452
                     * based on the current row/column
2453
                     */
2454
                    r = parseInt(i/6)*3 + parseInt(j/6);
2455
                    g = j%6;
2456
                    b = i%6;
2457
                }
2458
                var bgColor = '#'+this.options.hexColors[r]+this.options.hexColors[g]+this.options.hexColors[b];
2459

    
2460
                var td = new Element('td');
2461
                if (!bSkip) {
2462
                    td.setStyle('backgroundColor', bgColor);
2463

    
2464
                    var a = new Element('a', {
2465
                        'class': 'colorSwatch ' + (((r > 2 && g > 2) || (r > 2 && b > 2) || (g > 2 && b > 2)) ? 'borderBlack': 'borderWhite'),
2466
                        'href':'javascript:void(0)',
2467
                        'title':bgColor,
2468
                        'alt':bgColor,
2469
                        events: {
2470
                            'mouseover': swatchOver,
2471
                            'click': swatchClick
2472
                        }
2473
                    });
2474
                    a.store('swatchColor', bgColor);
2475
                    td.adopt(a);
2476
                } else {
2477
                    var span = new Element('span', {'class':'emptyCell'});
2478
                    td.adopt(span);
2479
                }
2480
                tr.adopt(td);
2481
            }
2482
            tbody.adopt(tr);
2483
        }
2484
        this.domObj.adopt(table);
2485
        this.updateSelected();
2486
        if (this.options.parent) {
2487
            this.addTo(this.options.parent);
2488
        }
2489
    },
2490

    
2491
    /**
2492
     * Method: swatchOver
2493
     * handle the mouse moving over a colour swatch by updating the preview
2494
     *
2495
     * Parameters:
2496
     * e - {Event} the mousemove event object
2497
     */
2498
    swatchOver: function(e) {
2499
        var a = e.target;
2500

    
2501
        this.previewSwatch.setStyle('backgroundColor', a.retrieve('swatchColor'));
2502
    },
2503

    
2504
    /**
2505
     * Method: swatchClick
2506
     * handle mouse click on a swatch by updating the color and hiding the
2507
     * panel.
2508
     *
2509
     * Parameters:
2510
     * e - {Event} the mouseclick event object
2511
     */
2512
    swatchClick: function(e) {
2513
        var a = e.target;
2514

    
2515
        this.options.color = a.retrieve('swatchColor');
2516
        this.updateSelected();
2517
        this.fireEvent('click', this);
2518
    },
2519

    
2520
    /**
2521
     * Method: changed
2522
     * handle the user entering a new colour value manually by updating the
2523
     * selected colour if the entered value is valid HEX.
2524
     */
2525
    changed: function() {
2526
        var color = this.colorInput.value;
2527
        if (color.substring(0,1) == '#') {
2528
            color = color.substring(1);
2529
        }
2530
        if (color.toLowerCase().match(/^[0-9a-f]{6}$/)) {
2531
            this.options.color = '#' +color.toUpperCase();
2532
            this.updateSelected();
2533
        }
2534
    },
2535

    
2536
    /**
2537
     * Method: alphaChanged
2538
     * handle the user entering a new alpha value manually by updating the
2539
     * selected alpha if the entered value is valid alpha (0-100).
2540
     */
2541
    alphaChanged: function() {
2542
        var alpha = this.alphaInput.value;
2543
        if (alpha.match(/^[0-9]{1,3}$/)) {
2544
            this.options.alpha = parseFloat(alpha/100);
2545
            this.updateSelected();
2546
        }
2547
    },
2548

    
2549
    /**
2550
     * Method: setColor
2551
     * set the colour represented by this colour panel
2552
     *
2553
     * Parameters:
2554
     * color - {String} the new hex color value
2555
     */
2556
    setColor: function( color ) {
2557
        this.colorInput.value = color;
2558
        this.changed();
2559
    },
2560

    
2561
    /**
2562
     * Method: setAlpha
2563
     * set the alpha represented by this colour panel
2564
     *
2565
     * Parameters:
2566
     * alpha - {Integer} the new alpha value (between 0 and 100)
2567
     */
2568
    setAlpha: function( alpha ) {
2569
        this.alphaInput.value = alpha;
2570
        this.alphaChanged();
2571
    },
2572

    
2573
    /**
2574
     * Method: updateSelected
2575
     * update the colour panel user interface based on the current
2576
     * colour and alpha values
2577
     */
2578
    updateSelected: function() {
2579
        var styles = {'backgroundColor':this.options.color};
2580

    
2581
        this.colorInput.value = this.options.color.substring(1);
2582

    
2583
        this.alphaInput.value = parseInt(this.options.alpha*100);
2584
        if (this.options.alpha < 1) {
2585
            styles.opacity = this.options.alpha;
2586
            styles.filter = 'Alpha(opacity='+(this.options.alpha*100)+')';
2587
        } else {
2588
            styles.opacity = '';
2589
            styles.filter = '';
2590
        }
2591
        this.selectedSwatch.setStyles(styles);
2592
        this.previewSwatch.setStyles(styles);
2593
        this.fireEvent('change', this);
2594
    }
2595
});
2596

    
2597
// $Id: color.js 423 2009-05-12 12:37:56Z pagameba $ 
2598
/**
2599
 * Class: Jx.Button.Color
2600
 *
2601
 * Extends: <Jx.Button.Flyout>
2602
 *
2603
 * A <Jx.ColorPalette> wrapped up in a Jx.Button.  The button includes a
2604
 * preview of the currently selected color.  Clicking the button opens
2605
 * the color panel.
2606
 *
2607
 * A color button is essentially a <Jx.Button.Flyout> where the content
2608
 * of the flyout is a <Jx.ColorPalette>.  For performance, all color buttons
2609
 * share an instance of <Jx.ColorPalette> which means only one button can be
2610
 * open at a time.  This isn't a huge restriction as flyouts already close
2611
 * each other when opened.
2612
 *
2613
 * Example:
2614
 * (code)
2615
 * var colorButton = new Jx.Button.Color({
2616
 *     onChange: function(button) {
2617
 *         console.log('color:' + button.options.color + ' alpha: ' +
2618
 *                     button.options.alpha);
2619
 *     }
2620
 * });
2621
 * (end)
2622
 *
2623
 * Events:
2624
 * change - fired when the color is changed.
2625
 *
2626
 * License: 
2627
 * Copyright (c) 2008, DM Solutions Group Inc.
2628
 * 
2629
 * This file is licensed under an MIT style license
2630
 */
2631
Jx.Button.Color = new Class({
2632
    Family: 'Jx.Button.Color',
2633
    Extends: Jx.Button.Flyout,
2634
    
2635
    swatch: null,
2636
     
2637
    options: {
2638
        /**
2639
         * Option: color
2640
         * a color to initialize the panel with, defaults to #000000
2641
         * (black) if not specified.
2642
         */
2643
        color: '#000000',
2644
        /**
2645
         * Option: alpha
2646
         * an alpha value to initialize the panel with, defaults to 1
2647
         *  (opaque) if not specified.
2648
         *
2649
         */
2650
        alpha: 100
2651
    },
2652

    
2653
    /**
2654
     * Constructor: Jx.Button.Color
2655
     * initialize a new color button.
2656
     *
2657
     * Parameters:
2658
     * options - <Jx.Button.Color.Options> initialize instance options.
2659
     *
2660
     */
2661
    initialize: function(options) {
2662
        if (!Jx.Button.Color.ColorPalette) {
2663
            Jx.Button.Color.ColorPalette = new Jx.ColorPalette(this.options);
2664
        }
2665
        var d = new Element('span', {'class':'jxButtonSwatch'});
2666

    
2667
        this.selectedSwatch = new Element('span');
2668
        d.appendChild(this.selectedSwatch);
2669

    
2670
        this.colorChangeFn = this.changed.bind(this);
2671
        this.hideFn = this.hide.bind(this);
2672
        /* we need to have an image to replace, but if a label is 
2673
           requested, there wouldn't normally be an image. */
2674
        options.image = Jx.aPixel.src;
2675

    
2676
        /* now we can safely initialize */
2677
        this.parent(options);
2678
        
2679
        // now replace the image with our swatch
2680
        d.replaces(this.domImg);
2681
        this.updateSwatch();
2682
    },
2683

    
2684
    /**
2685
     * Method: clicked
2686
     * override <Jx.Button.Flyout> to use a singleton color palette.
2687
     */
2688
    clicked: function() {
2689
        if (Jx.Button.Color.ColorPalette.currentButton) {
2690
            Jx.Button.Color.ColorPalette.currentButton.hide();
2691
        }
2692
        Jx.Button.Color.ColorPalette.currentButton = this;
2693
        Jx.Button.Color.ColorPalette.addEvent('change', this.colorChangeFn);
2694
        Jx.Button.Color.ColorPalette.addEvent('click', this.hideFn);
2695
        this.content.appendChild(Jx.Button.Color.ColorPalette.domObj);
2696
        Jx.Button.Color.ColorPalette.domObj.setStyle('display', 'block');
2697
        Jx.Button.Flyout.prototype.clicked.apply(this, arguments);
2698
        /* setting these before causes an update problem when clicking on
2699
         * a second color button when another one is open - the color
2700
         * wasn't updating properly
2701
         */
2702
         
2703
        Jx.Button.Color.ColorPalette.options.color = this.options.color;
2704
        Jx.Button.Color.ColorPalette.options.alpha = this.options.alpha/100;
2705
        Jx.Button.Color.ColorPalette.updateSelected();
2706
},
2707

    
2708
    /**
2709
     * Method: hide
2710
     * hide the color panel
2711
     */
2712
    hide: function() {
2713
        this.setActive(false);
2714
        Jx.Button.Color.ColorPalette.removeEvent('change', this.colorChangeFn);
2715
        Jx.Button.Color.ColorPalette.removeEvent('click', this.hideFn);
2716
        Jx.Button.Flyout.prototype.hide.apply(this, arguments);
2717
        Jx.Button.Color.ColorPalette.currentButton = null;
2718
    },
2719

    
2720
    /**
2721
     * Method: setColor
2722
     * set the color represented by this color panel
2723
     *
2724
     * Parameters:
2725
     * color - {String} the new hex color value
2726
     */
2727
    setColor: function(color) {
2728
        this.options.color = color;
2729
        this.updateSwatch();
2730
    },
2731

    
2732
    /**
2733
     * Method: setAlpha
2734
     * set the alpha represented by this color panel
2735
     *
2736
     * Parameters:
2737
     * alpha - {Integer} the new alpha value (between 0 and 100)
2738
     */
2739
    setAlpha: function(alpha) {
2740
        this.options.alpha = alpha;
2741
        this.updateSwatch();
2742
    },
2743

    
2744
    /**
2745
     * Method: changed
2746
     * handle the color changing in the palette by updating the preview swatch
2747
     * in the button and firing the change event.
2748
     *
2749
     * Parameters:
2750
     * panel - <Jx.ColorPalette> the palette that changed.
2751
     */
2752
    changed: function(panel) {
2753
        var changed = false;
2754
        if (this.options.color != panel.options.color) {
2755
            this.options.color = panel.options.color;
2756
            changed = true;
2757
        }
2758
        if (this.options.alpha != panel.options.alpha * 100) {
2759
            this.options.alpha = panel.options.alpha * 100;
2760
            changed = true;
2761
        }
2762
        if (changed) {
2763
            this.updateSwatch();
2764
            this.fireEvent('change',this);
2765
        }
2766
    },
2767

    
2768
    /**
2769
     * Method: updateSwatch
2770
     * Update the swatch color for the current color
2771
     */
2772
    updateSwatch: function() {
2773
        var styles = {'backgroundColor':this.options.color};
2774
        if (this.options.alpha < 100) {
2775
            styles.filter = 'Alpha(opacity='+(this.options.alpha)+')';
2776
            styles.opacity = this.options.alpha / 100;
2777
            
2778
        } else {
2779
            styles.opacity = '';
2780
            styles.filter = '';
2781
        }
2782
        this.selectedSwatch.setStyles(styles);
2783
    }
2784
});
2785
// $Id: menu.js 423 2009-05-12 12:37:56Z pagameba $
2786
/**
2787
 * Class: Jx.Menu
2788
 *
2789
 * Extends: Object
2790
 *
2791
 * Implements: Options, Events, <Jx.AutoPosition>, <Jx.Chrome>, <Jx.Addable>
2792
 *
2793
 * A main menu as opposed to a sub menu that lives inside the menu.
2794
 *
2795
 * TODO: Jx.Menu
2796
 * revisit this to see if Jx.Menu and Jx.SubMenu can be merged into
2797
 * a single implementation.
2798
 *
2799
 * Example:
2800
 * (code)
2801
 * (end)
2802
 *
2803
 * License: 
2804
 * Copyright (c) 2008, DM Solutions Group Inc.
2805
 * 
2806
 * This file is licensed under an MIT style license
2807
 */
2808
Jx.Menu = new Class({
2809
    Family: 'Jx.Menu',
2810
    /**
2811
     * Implements:
2812
     * * Options
2813
     * * Events
2814
     * * <Jx.AutoPosition>
2815
     * * <Jx.Chrome>
2816
     * * <Jx.Addable>
2817
     */
2818
    Implements: [Options, Events, Jx.AutoPosition, Jx.Chrome, Jx.Addable],
2819
    /**
2820
     * Property: domObj
2821
     * {HTMLElement} The HTML element containing the menu.
2822
     */
2823
    domObj : null,
2824
    /**
2825
     * Property: button
2826
     * {<Jx.Button>} The button that represents this menu in a toolbar and
2827
     * opens the menu.
2828
     */
2829
    button : null,
2830
    /**
2831
     * Property: subDomObj
2832
     * {HTMLElement} the HTML element that contains the menu items
2833
     * within the menu.
2834
     */
2835
    subDomObj : null,
2836
    /**
2837
     * Property: items
2838
     * {Array} the items in this menu
2839
     */
2840
    items : null,
2841
    /**
2842
     * Constructor: Jx.Menu
2843
     * Create a new instance of Jx.Menu.
2844
     *
2845
     * Parameters:
2846
     * options - see <Jx.Button.Options>.  If no options are provided then
2847
     * no button is created.
2848
     */
2849
    initialize : function(options) {
2850
        this.setOptions(options);
2851
        if (!Jx.Menu.Menus) {
2852
            Jx.Menu.Menus = [];
2853
        }
2854
        /* stores menu items and sub menus */
2855
        this.items = [];
2856
        
2857
        this.contentContainer = new Element('div',{
2858
            'class':'jxMenuContainer',
2859
            events: {
2860
                contextmenu: function(e){e.stop();}
2861
            }
2862
        });
2863
        
2864
        /* the DOM element that holds the actual menu */
2865
        this.subDomObj = new Element('ul',{
2866
            'class':'jxMenu'
2867
        });
2868
        
2869
        this.contentContainer.adopt(this.subDomObj);
2870
        
2871
        /* if options are passed, make a button inside an LI so the
2872
           menu can be embedded inside a toolbar */
2873
        if (options) {
2874
            this.button = new Jx.Button($merge(options,{
2875
                onClick:this.show.bind(this)
2876
            }));
2877
            this.button.domA.addClass('jxButtonMenu');
2878
            this.button.domA.addEvent('mouseover', this.onMouseOver.bindWithEvent(this));
2879
            
2880
            this.domObj = this.button.domObj;
2881
        }
2882
        
2883
        /* pre-bind the hide function for efficiency */
2884
        this.hideWatcher = this.hide.bindWithEvent(this);
2885
        this.keypressWatcher = this.keypressHandler.bindWithEvent(this);
2886
        
2887
        if (this.options.parent) {
2888
            this.addTo(this.options.parent);
2889
        }
2890
    },
2891
    /**
2892
     * Method: add
2893
     * Add menu items to the sub menu.
2894
     *
2895
     * Parameters:
2896
     * item - {<Jx.MenuItem>} the menu item to add.  Multiple menu items
2897
     * can be added by passing multiple arguments to this function.
2898
     */
2899
    add : function() {
2900
        $A(arguments).flatten().each(function(item){
2901
            this.items.push(item);
2902
            item.setOwner(this);
2903
            this.subDomObj.adopt(item.domObj);
2904
        }, this);
2905
        return this;
2906
    },
2907
    /**
2908
     * Method: deactivate
2909
     * Deactivate the menu by hiding it.
2910
     */
2911
    deactivate: function() {this.hide();},
2912
    /**
2913
     * Method: onMouseOver
2914
     * Handle the user moving the mouse over the button for this menu
2915
     * by showing this menu and hiding the other menu.
2916
     *
2917
     * Parameters:
2918
     * e - {Event} the mouse event
2919
     */
2920
    onMouseOver: function(e) {
2921
        if (Jx.Menu.Menus[0] && Jx.Menu.Menus[0] != this) {
2922
            this.show({event:e});
2923
        }
2924
    },
2925
    
2926
    /**
2927
     * Method: eventInMenu
2928
     * determine if an event happened inside this menu or a sub menu
2929
     * of this menu.
2930
     *
2931
     * Parameters:
2932
     * e - {Event} the mouse event
2933
     *
2934
     * Returns:
2935
     * {Boolean} true if the event happened in the menu or
2936
     * a sub menu of this menu, false otherwise
2937
     */
2938
    eventInMenu: function(e) {
2939
        var target = $(e.target);
2940
        if (!target) {
2941
            return false;
2942
        }
2943
        if (target.descendantOf(this.domObj) ||
2944
            target.descendantOf(this.subDomObj)) {
2945
            return true;
2946
        } else {
2947
            var ul = target.findElement('ul');
2948
            if (ul) {
2949
                var sm = ul.retrieve('jxSubMenu');
2950
                if (sm) {
2951
                    var owner = sm.owner;
2952
                    while (owner) {
2953
                        if (owner == this) {
2954
                            return true;
2955
                        }
2956
                        owner = owner.owner;
2957
                    }
2958
                }
2959
            }
2960
            return false;
2961
        }
2962
    },
2963
    
2964
    /**
2965
     * Method: hide
2966
     * Hide the menu.
2967
     *
2968
     * Parameters:
2969
     * e - {Event} the mouse event
2970
     */
2971
    hide: function(e) {
2972
        if (e) {
2973
            if (this.visibleItem && this.visibleItem.eventInMenu) {
2974
                if (this.visibleItem.eventInMenu(e)) {
2975
                    return;
2976
                }
2977
            } else if (this.eventInMenu(e)) {
2978
                return;
2979
            }
2980
        }
2981
        if (Jx.Menu.Menus[0] && Jx.Menu.Menus[0] == this) {
2982
            Jx.Menu.Menus[0] = null;
2983
        }
2984
        if (this.button && this.button.domA) {
2985
            this.button.domA.removeClass('jx'+this.button.options.type+'Active');            
2986
        }
2987
        this.items.each(function(item){item.hide(e);});
2988
        document.removeEvent('mousedown', this.hideWatcher);
2989
        document.removeEvent('keydown', this.keypressWatcher);
2990
        this.contentContainer.setStyle('display','none');
2991
        this.fireEvent('hide', this); 
2992
    },
2993
    /**
2994
     * Method: show
2995
     * Show the menu
2996
     *
2997
     * Parameters:
2998
     * e - {Event} the mouse event
2999
     */
3000
    show : function(o) {
3001
        var e = o.event;
3002
        if (Jx.Menu.Menus[0]) {
3003
            if (Jx.Menu.Menus[0] != this) {
3004
                Jx.Menu.Menus[0].button.blur();
3005
                Jx.Menu.Menus[0].hide(e);
3006
            } else {
3007
                this.hide();
3008
                return;
3009
            }  
3010
        } 
3011
        if (this.items.length === 0) {
3012
            return;
3013
        }
3014
        Jx.Menu.Menus[0] = this;
3015
        this.button.focus();
3016
        this.contentContainer.setStyle('visibility','hidden');
3017
        this.contentContainer.setStyle('display','block');
3018
        $(document.body).adopt(this.contentContainer);            
3019
        /* we have to size the container for IE to render the chrome correctly
3020
         * but just in the menu/sub menu case - there is some horrible peekaboo
3021
         * bug in IE related to ULs that we just couldn't figure out
3022
         */
3023
        this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
3024
        this.showChrome(this.contentContainer);
3025
        
3026
        this.position(this.contentContainer, this.button.domObj, {
3027
            horizontal: ['left left'],
3028
            vertical: ['bottom top', 'top bottom'],
3029
            offsets: this.chromeOffsets
3030
        });
3031

    
3032
        this.contentContainer.setStyle('visibility','');
3033
        
3034
        if (this.button && this.button.domA) {
3035
            this.button.domA.addClass('jx'+this.button.options.type+'Active');            
3036
        }
3037
        if (e) {
3038
            //why were we doing this? it is affecting the closing of
3039
            //other elements like flyouts (issue 13)
3040
            //e.stop();
3041
        }
3042
        /* fix bug in IE that closes the menu as it opens because of bubbling */
3043
        document.addEvent('mousedown', this.hideWatcher);
3044
        document.addEvent('keydown', this.keypressWatcher);
3045
        this.fireEvent('show', this); 
3046
    },
3047
    /**
3048
     * Method: setVisibleItem
3049
     * Set the sub menu that is currently open
3050
     *
3051
     * Parameters:
3052
     * obj- {<Jx.SubMenu>} the sub menu that just became visible
3053
     */
3054
    setVisibleItem: function(obj) {
3055
        if (this.visibleItem != obj) {
3056
            if (this.visibleItem && this.visibleItem.hide) {
3057
                this.visibleItem.hide();
3058
            }
3059
            this.visibleItem = obj;
3060
            this.visibleItem.show();
3061
        }
3062
    },
3063

    
3064
    /* hide flyout if the user presses the ESC key */
3065
    keypressHandler: function(e) {
3066
        e = new Event(e);
3067
        if (e.key == 'esc') {
3068
            this.hide();
3069
        }
3070
    }
3071
});
3072

    
3073
// $Id: set.js 423 2009-05-12 12:37:56Z pagameba $
3074
/**
3075
 * Class: Jx.ButtonSet
3076
 *
3077
 * Extends: Object
3078
 *
3079
 * Implements: Options, Events
3080
 *
3081
 * A ButtonSet manages a set of <Jx.Button> instances by ensuring that only one
3082
 * of the buttons is active.  All the buttons need to have been created with
3083
 * the toggle option set to true for this to work.
3084
 *
3085
 * Example:
3086
 * (code)
3087
 * var toolbar = new Jx.Toolbar('bar');
3088
 * var buttonSet = new Jx.ButtonSet();
3089
 * 
3090
 * var tab1 = new Jx.Button({label: 'b1', toggle:true, contentID: 'content1'});
3091
 * var tab2 = new Jx.Button({label: 'b2', toggle:true, contentID: 'content2'});
3092
 * var tab3 = new Jx.Button({label: 'b3', toggle:true, contentID: 'content3'});
3093
 * var tab4 = new Jx.Button({label: 'b4', toggle:true, contentURL: 'test_content.html'});
3094
 * 
3095
 * buttonSet.add(b1,b2,b3,b4);
3096
 * (end)
3097
 *
3098
 * Events:
3099
 * change - the current button has changed
3100
 *
3101
 * License: 
3102
 * Copyright (c) 2008, DM Solutions Group Inc.
3103
 * 
3104
 * This file is licensed under an MIT style license
3105
 */
3106
Jx.ButtonSet = new Class({
3107
    Family: 'Jx.ButtonSet',
3108
    Implements: [Options,Events],
3109
    /**
3110
     * Property: buttons
3111
     * {Array} array of buttons that are managed by this button set
3112
     */
3113
    buttons: null,
3114
    /**
3115
     * Constructor: Jx.ButtonSet
3116
     * Create a new instance of <Jx.ButtonSet>
3117
     *
3118
     * Parameters:
3119
     * options - an options object, only event handlers are supported
3120
     * as options at this time.
3121
     */
3122
    initialize : function(options) {
3123
        this.setOptions(options);
3124
        this.buttons = [];
3125
        this.buttonChangedHandler = this.buttonChanged.bind(this);
3126
    },
3127
    
3128
    /**
3129
     * Method: add
3130
     * Add one or more <Jx.Button>s to the ButtonSet.
3131
     *
3132
     * Parameters:
3133
     * button - {<Jx.Button>} an instance of <Jx.Button> to add to the button set.  More
3134
     * than one button can be added by passing extra parameters to this method.
3135
     */
3136
    add : function() {
3137
        $A(arguments).each(function(button) {
3138
            if (button.domObj.hasClass('jx'+button.options.type+'Toggle')) {
3139
                button.domObj.removeClass('jx'+button.options.type+'Toggle');
3140
                button.domObj.addClass('jx'+button.options.type+'Set');                
3141
            }
3142
            button.addEvent('down',this.buttonChangedHandler);
3143
            var that = this;
3144
            button.setActive = function(active) {
3145
                if (this.options.active && that.activeButton == this) {
3146
                    return;
3147
                } else {
3148
                    Jx.Button.prototype.setActive.apply(this, [active]);
3149
                }
3150
            };
3151
            if (!this.activeButton || button.options.active) {
3152
                button.options.active = false;
3153
                button.setActive(true);
3154
            }
3155
            this.buttons.push(button);
3156
        }, this);
3157
        return this;
3158
    },
3159
    /**
3160
     * Method: remove
3161
     * Remove a button from this Button.
3162
     *
3163
     * Parameters:
3164
     * button - {<Jx.Button>} the button to remove.
3165
     */
3166
    remove : function(button) {
3167
        this.buttons.erase(button);
3168
        if (this.activeButton == button) {
3169
            if (this.buttons.length) {
3170
                this.buttons[0].setActive(true);
3171
            }
3172
            button.removeEvent('down',this.buttonChangedHandler);
3173
            button.setActive = Jx.Button.prototype.setActive;
3174
        }
3175
    },
3176
    /**
3177
     * Method: setActiveButton
3178
     * Set the active button to the one passed to this method
3179
     *
3180
     * Parameters:
3181
     * button - {<Jx.Button>} the button to make active.
3182
     */
3183
    setActiveButton: function(button) {
3184
        var b = this.activeButton;
3185
        this.activeButton = button;
3186
        if (b && b != button) {
3187
            b.setActive(false);
3188
        }
3189
    },
3190
    /**
3191
     * Method: selectionChanged
3192
     * Handle selection changing on the buttons themselves and activate the
3193
     * appropriate button in response.
3194
     *
3195
     * Parameters:
3196
     * button - {<Jx.Button>} the button to make active.
3197
     */
3198
    buttonChanged: function(button) {
3199
        this.setActiveButton(button);
3200
        this.fireEvent('change', this);
3201
    }
3202
});
3203

    
3204

    
3205

    
3206
// $Id: multi.js 423 2009-05-12 12:37:56Z pagameba $
3207
/**
3208
 * Class: Jx.Button.Multi
3209
 *
3210
 * Extends: <Jx.Button>
3211
 *
3212
 * Implements: 
3213
 *
3214
 * Multi buttons are used to contain multiple buttons in a drop down list
3215
 * where only one button is actually visible and clickable in the interface.
3216
 * 
3217
 * When the user clicks the active button, it performs its normal action.
3218
 * The user may also click a drop-down arrow to the right of the button and
3219
 * access the full list of buttons.  Clicking a button in the list causes
3220
 * that button to replace the active button in the toolbar and performs
3221
 * the button's regular action.
3222
 *
3223
 * Other buttons can be added to the Multi button using the add method.
3224
 *
3225
 * This is not really a button, but rather a container for buttons.  The
3226
 * button structure is a div containing two buttons, a normal button and
3227
 * a flyout button.  The flyout contains a toolbar into which all the 
3228
 * added buttons are placed.  The main button content is cloned from the
3229
 * last button clicked (or first button added).
3230
 *
3231
 * The Multi button does not trigger any events itself, only the contained
3232
 * buttons trigger events.
3233
 *
3234
 * Example:
3235
 * (code)
3236
 * var b1 = new Jx.Button({
3237
 *     label: 'b1',
3238
 *     onClick: function(button) {
3239
 *         console.log('b1 clicked');
3240
 *     }
3241
 * });
3242
 * var b2 = new Jx.Button({
3243
 *     label: 'b2',
3244
 *     onClick: function(button) {
3245
 *         console.log('b2 clicked');
3246
 *     }
3247
 * });
3248
 * var b3 = new Jx.Button({
3249
 *     label: 'b3',
3250
 *     onClick: function(button) {
3251
 *         console.log('b3 clicked');
3252
 *     }
3253
 * });
3254
 * var multiButton = new Jx.Button.Multi();
3255
 * multiButton.add(b1, b2, b3);
3256
 * (end)
3257
 *
3258
 * License: 
3259
 * Copyright (c) 2008, DM Solutions Group Inc.
3260
 * 
3261
 * This file is licensed under an MIT style license
3262
 */
3263
Jx.Button.Multi = new Class({
3264
    Family: 'Jx.Button.Multi',
3265

    
3266
    Extends: Jx.Button,
3267
    /**
3268
     * Property: {<Jx.Button>} activeButton
3269
     * the currently selected button
3270
     */
3271
    activeButton: null,
3272
    /**
3273
     * Property: buttons
3274
     * {Array} the buttons added to this multi button
3275
     */
3276
    buttons: null,
3277
    /**
3278
     * Constructor: Jx.Button.Multi
3279
     * construct a new instance of Jx.Button.Multi.
3280
     */
3281
    initialize: function(opts) {
3282
        this.parent(opts);
3283

    
3284
        this.buttons = [];
3285

    
3286
        this.domA.addClass('jxButtonMulti');
3287
        
3288
        this.menu = new Jx.Menu();
3289
        this.menu.button = this;
3290
        this.buttonSet = new Jx.ButtonSet();
3291
        
3292
        this.clickHandler = this.clicked.bind(this);
3293
        
3294
        var a = new Element('a', {
3295
            'class': 'jxButtonDisclose',
3296
            'href': 'javascript:void(0)'
3297
        });
3298
        var button = this;
3299
        var hasFocus;
3300
        
3301
        a.addEvents({
3302
            'click': (function(e) {
3303
                if (this.items.length === 0) {
3304
                    return;
3305
                }
3306
                if (!button.options.enabled) {
3307
                    return;
3308
                }
3309
                this.contentContainer.setStyle('visibility','hidden');
3310
                this.contentContainer.setStyle('display','block');
3311
                $(document.body).adopt(this.contentContainer);            
3312
                /* we have to size the container for IE to render the chrome correctly
3313
                 * but just in the menu/sub menu case - there is some horrible peekaboo
3314
                 * bug in IE related to ULs that we just couldn't figure out
3315
                 */
3316
                this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
3317

    
3318
                this.showChrome(this.contentContainer);
3319

    
3320
                this.position(this.contentContainer, this.button.domObj, {
3321
                    horizontal: ['right right'],
3322
                    vertical: ['bottom top', 'top bottom'],
3323
                    offsets: this.chromeOffsets
3324
                });
3325

    
3326
                this.contentContainer.setStyle('visibility','');
3327

    
3328
                document.addEvent('mousedown', this.hideWatcher);
3329
                document.addEvent('keyup', this.keypressWatcher);
3330
                
3331
                /* why were we doing this? It was affecting issue 13
3332
                if (e.$extended) {
3333
                    e.stop();                                        
3334
                } else if (e.event && e.event.$extended) {
3335
                    e.event.stop();
3336
                }
3337
                */
3338
                this.fireEvent('show', this);
3339
            }).bindWithEvent(this.menu),
3340
            'mouseenter':(function(){
3341
                $(this.domObj.firstChild).addClass('jxButtonHover');
3342
                if (hasFocus) {
3343
                    a.addClass('jx'+this.options.type+'Pressed');
3344
                }
3345
            }).bind(this),
3346
            'mouseleave':(function(){
3347
                $(this.domObj.firstChild).removeClass('jxButtonHover');
3348
                a.removeClass('jx'+this.options.type+'Pressed');
3349
            }).bind(this),
3350
            mousedown: (function(e) {
3351
                a.addClass('jx'+this.options.type+'Pressed');
3352
                hasFocus = true;
3353
                this.focus();
3354
            }).bindWithEvent(this),
3355
            mouseup: (function(e) {
3356
                a.removeClass('jx'+this.options.type+'Pressed');                  
3357
            }).bindWithEvent(this),
3358
            keydown: (function(e) {
3359
                if (e.key == 'enter') {
3360
                    a.addClass('jx'+this.options.type+'Pressed');
3361
                }
3362
            }).bindWithEvent(this),
3363
            keyup: (function(e) {
3364
                if (e.key == 'enter') {
3365
                    a.removeClass('jx'+this.options.type+'Pressed');
3366
                }
3367
            }).bindWithEvent(this),
3368
            blur: function() { hasFocus = false; }
3369
            
3370
        });
3371
        if (typeof Drag != 'undefined') {
3372
            new Drag(a, {
3373
                onStart: function() {this.stop();}
3374
            });
3375
        }
3376
        
3377
        this.menu.addEvents({
3378
            'show': (function() {
3379
                this.domA.addClass('jxButtonActive');                    
3380
            }).bind(this),
3381
            'hide': (function() {
3382
                if (this.options.active) {
3383
                    this.domA.addClass('jxButtonActive');                    
3384
                }
3385
            }).bind(this)
3386
        });
3387
        a.adopt(new Element('img', {
3388
            src: Jx.aPixel.src,
3389
            alt: '',
3390
            title: ''
3391
        }));
3392
        this.domObj.adopt(a);
3393
        this.discloser = a;
3394
        if (this.options.items) {
3395
            this.add(this.options.items);
3396
        }
3397
    },
3398
    /**
3399
     * Method: add
3400
     * adds one or more buttons to the Multi button.  The first button
3401
     * added becomes the active button initialize.  This function 
3402
     * takes a variable number of arguments, each of which is expected
3403
     * to be an instance of <Jx.Button>.
3404
     *
3405
     * Parameters: 
3406
     * button - {<Jx.Button>} a <Jx.Button> instance, may be repeated in the parameter list
3407
     */
3408
    add: function() {
3409
        $A(arguments).flatten().each(function(theButton){
3410
            if (!theButton instanceof Jx.Button) {
3411
                return;
3412
            }
3413
            this.buttons.push(theButton);
3414
            var f = this.setButton.bind(this, theButton);
3415
            var opts = $merge(
3416
                theButton.options,
3417
                { toggle: true, onClick: f}
3418
            );
3419
            if (!opts.label) {
3420
                opts.label = '&nbsp;';
3421
            }
3422
            if (!opts.image || opts.image.indexOf('a_pixel') != -1) {
3423
                delete opts.image;
3424
            }
3425
            var button = new Jx.Menu.Item(opts);
3426
            this.buttonSet.add(button);
3427
            this.menu.add(button);
3428
            theButton.multiButton = button;
3429
            theButton.domA.addClass('jxButtonMulti');
3430
            if (!this.activeButton) {
3431
                this.domA.dispose();
3432
                this.setActiveButton(theButton);
3433
            }
3434
        }, this);
3435
    },
3436
    /**
3437
     * Method: remove
3438
     * remove a button from a multi button
3439
     *
3440
     * Parameters:
3441
     * button - {<Jx.Button>} the button to remove
3442
     */
3443
    remove: function(button) {
3444
        if (!button || !button.multiButton) {
3445
            return;
3446
        }
3447
        // the toolbar will only remove the li.toolItem, which is
3448
        // the parent node of the multiButton's domObj.
3449
        if (this.menu.remove(button.multiButton)) {
3450
            button.multiButton = null;
3451
            if (this.activeButton == button) {
3452
                // if any buttons are left that are not this button
3453
                // then set the first one to be the active button
3454
                // otherwise set the active button to nothing
3455
                if (!this.buttons.some(function(b) {
3456
                    if (b != button) {
3457
                        this.setActiveButton(b);
3458
                        return true;
3459
                    } else {
3460
                        return false;
3461
                    }
3462
                }, this)) {
3463
                    this.setActiveButton(null);
3464
                }
3465
            }
3466
            this.buttons.erase(button);
3467
        }
3468
    },
3469
    /**
3470
     * Method: setActiveButton
3471
     * update the menu item to be the requested button.
3472
     *
3473
     * Parameters: 
3474
     * button - {<Jx.Button>} a <Jx.Button> instance that was added to this multi button.
3475
     */
3476
    setActiveButton: function(button) {
3477
        if (this.activeButton) {
3478
            this.activeButton.domA.dispose();
3479
            this.activeButton.domA.removeEvent(this.clickHandler);
3480
        }
3481
        if (button && button.domA) {
3482
            this.domObj.grab(button.domA, 'top');
3483
            this.domA = button.domA;
3484
            this.domA.addEvent('click', this.clickHandler);
3485
            if (this.options.toggle) {
3486
                this.options.active = false;
3487
                this.setActive(true); 
3488
            }         
3489
        }
3490
        this.activeButton = button;
3491
    },
3492
    /**
3493
     * Method: setButton
3494
     * update the active button in the menu item, trigger the button's action
3495
     * and hide the flyout that contains the buttons.
3496
     *
3497
     * Parameters: 
3498
     * button - {<Jx.Button>} The button to set as the active button
3499
     */
3500
    setButton: function(button) {
3501
        this.setActiveButton(button);
3502
        button.clicked();
3503
    }
3504
});// $Id: menu.item.js 423 2009-05-12 12:37:56Z pagameba $
3505
/**
3506
 * Class: Jx.Menu.Item
3507
 *
3508
 * Extends: <Jx.Button>
3509
 *
3510
 * A menu item is a single entry in a menu.  It is typically composed of
3511
 * a label and an optional icon.  Selecting the menu item emits an event.
3512
 *
3513
 * Jx.Menu.Item is represented by a <Jx.Button> with type MenuItem and the
3514
 * associated CSS changes noted in <Jx.Button>.  The container of a MenuItem
3515
 * is an 'li' element.
3516
 *
3517
 * Example:
3518
 * (code)
3519
 * (end)
3520
 *
3521
 * Events:
3522
 * click - fired when the menu item is clicked.
3523
 *
3524
 * License: 
3525
 * Copyright (c) 2008, DM Solutions Group Inc.
3526
 * 
3527
 * This file is licensed under an MIT style license
3528
 */
3529
Jx.Menu.Item = new Class({
3530
    Family: 'Jx.Menu.Item',
3531
    Extends: Jx.Button,
3532
    /**
3533
     * Property: owner
3534
     * {<Jx.SubMenu> or <Jx.Menu>} the menu that contains the menu item.
3535
     */
3536
    owner: null,
3537
    options: {
3538
        enabled: true,
3539
        image: null,
3540
        label: '&nbsp;',
3541
        toggleClass: 'Toggle'
3542
    },
3543
    /**
3544
     * Constructor: Jx.Menu.Item
3545
     * Create a new instance of Jx.Menu.Item
3546
     *
3547
     * Parameters:
3548
     * options - See <Jx.Button.Options>
3549
     */
3550
    initialize: function(options) {
3551
        this.parent($merge({
3552
                image: Jx.aPixel.src
3553
            },
3554
            options, {
3555
                container:'li',
3556
                type:'MenuItem',
3557
                toggleClass: (options.image ? null : this.options.toggleClass)
3558
            }
3559
        ));
3560
        this.domObj.addEvent('mouseover', this.onMouseOver.bindWithEvent(this));
3561
    },
3562
    /**
3563
     * Method: setOwner
3564
     * Set the owner of this menu item
3565
     *
3566
     * Parameters:
3567
     * obj - {Object} the new owner
3568
     */
3569
    setOwner: function(obj) {
3570
        this.owner = obj;
3571
    },
3572
    /**
3573
     * Method: hide
3574
     * Hide the menu item.
3575
     */
3576
    hide: function() {this.blur();},
3577
    /**
3578
     * Method: show
3579
     * Show the menu item
3580
     */
3581
    show: $empty,
3582
    /**
3583
     * Method: clicked
3584
     * Handle the user clicking on the menu item, overriding the <Jx.Button::clicked>
3585
     * method to facilitate menu tracking
3586
     *
3587
     * Parameters:
3588
     * obj - {Object} an object containing an event property that was the user
3589
     * event.
3590
     */
3591
    clicked: function(obj) {
3592
        if (this.options.enabled) {
3593
            if (this.options.toggle) {
3594
                this.setActive(!this.options.active);
3595
            }
3596
            this.fireEvent('click', this);
3597
            if (this.owner && this.owner.deactivate) {
3598
                this.owner.deactivate(obj.event);
3599
            }
3600
        }
3601
    },
3602
    /**
3603
     * Method: onmouseover
3604
     * handle the mouse moving over the menu item
3605
     *
3606
     * Parameters:
3607
     * e - {Event} the mousemove event
3608
     */
3609
    onMouseOver: function(e) {
3610
        if (this.owner && this.owner.setVisibleItem) {
3611
            this.owner.setVisibleItem(this);
3612
        }
3613
        this.show(e);
3614
    }
3615
});
3616

    
3617
// $Id: combo.js 423 2009-05-12 12:37:56Z pagameba $
3618
/**
3619
 * Class: Jx.Button.Combo
3620
 *
3621
 * Extends: <Jx.Button.Multi>
3622
 *
3623
 * A drop down list of selectable items.  Items can be either a string, an image or both.
3624
 *
3625
 * Example:
3626
 * (code)
3627
 * new Jx.Button.Combo({
3628
 *     label: 'Choose a symbol',
3629
 *     items: [
3630
 *         {label: 'Star', image: 'images/swatches.png', imageClass: 'comboStar'},
3631
 *         {label: 'Square', image: 'images/swatches.png', imageClass: 'comboSquare'},
3632
 *         {label: 'Triangle', image: 'images/swatches.png', imageClass: 'comboTriangle'},
3633
 *         {label: 'Circle', image: 'images/swatches.png', imageClass: 'comboCircle'},
3634
 *         {label: 'Plus', image: 'images/swatches.png', imageClass: 'comboPlus'},
3635
 *         {label: 'Cross', image: 'images/swatches.png', imageClass: 'comboCross'}
3636
 *     ],
3637
 *     onChange: function(combo) { alert('you selected ' + combo.getValue()) }
3638
 * })
3639
 * (end)
3640
 *
3641
 * Events:
3642
 * change - triggered when the user selects a new item from the list
3643
 *
3644
 * License: 
3645
 * Copyright (c) 2008, DM Solutions Group Inc.
3646
 * 
3647
 * This file is licensed under an MIT style license
3648
 */
3649
Jx.Button.Combo = new Class({
3650
    Family: 'Jx.Button.Combo',
3651
    Extends: Jx.Button.Multi,
3652
    domObj : null,
3653
    ul : null,
3654
    /**
3655
     * Property: currentSelection
3656
     * {Object} current selection in the list 
3657
     */
3658
    currentSelection : null,
3659
    
3660
    options: {
3661
        /* Option: editable
3662
         * boolean, default false.  Can the value be edited by the user?
3663
         */
3664
        editable: false,
3665
        /* Option: label
3666
         * string, default ''.  The label to display next to the combo.
3667
         */
3668
        label: ''
3669
    },
3670
    
3671
    /** 
3672
     * Constructor: Jx.Combo
3673
     * create a new instance of Jx.Combo
3674
     *
3675
     * Parameters:
3676
     * options - <Jx.button.Combo.Options>
3677
     */
3678
    initialize: function(options) {
3679
        this.parent(); //we don't want to pass options to parent
3680
        this.setOptions(options);
3681
        this.domA.removeClass('jxButtonMulti');
3682
        if (this.options.editable) {
3683
            // remove the button's normal A tag and replace it with a span
3684
            // so the input ends up not being inside an A tag - this was
3685
            // causing all kinds of problems for selecting text inside it
3686
            // due to some user-select: none classes that were introduced
3687
            // to make buttons not selectable in the first place.
3688
            //
3689
            // Ultimately I think we want to fix this so that the discloser
3690
            // in Jx.Button.Multi is a separate beast and we can use it here
3691
            // without inheriting from multi buttons
3692
            var s = new Element('span', {'class':'jxButton'});
3693
            s.adopt(this.domA.firstChild);
3694
            this.domA = s.replaces(this.domA);
3695
            this.domA.addClass('jxButtonComboDefault');
3696
            this.domA.addClass('jxButtonEditCombo');
3697
            this.domInput = new Element('input',{
3698
                type:'text',
3699
                events:{
3700
                    change: this.valueChanged.bindWithEvent(this),
3701
                    keydown: this.onKeyPress.bindWithEvent(this),
3702
                    focus: (function() {
3703
                        if (this.domA.hasClass('jxButtonComboDefault')) {
3704
                            this.domInput.value = '';
3705
                            this.domA.removeClass('jxButtonComboDefault');
3706
                        }
3707
                    }).bind(this)
3708
                },
3709
                value: this.options.label
3710
            });
3711
            this.domLabel.empty();
3712
            this.domLabel.addClass('jxComboInput');
3713
            this.domLabel.adopt(this.domInput);
3714
        } else {
3715
            this.discloser.dispose();
3716
            this.domA.addClass('jxButtonCombo');
3717
            this.addEvent('click', (function(e){
3718
                this.discloser.fireEvent('click', e);
3719
            }).bindWithEvent(this));
3720
        }
3721
        this.buttonSet = new Jx.ButtonSet({
3722
            onChange: (function(set) {
3723
                var button = set.activeButton;            
3724
                this.domA.removeClass('jxButtonComboDefault');
3725
                if (this.options.editable) {
3726
                    this.domInput.value = button.options.label;
3727
                } else {
3728
                    var l = button.options.label;
3729
                    if (l == '&nbsp;') {
3730
                        l = '';
3731
                    }
3732
                    this.setLabel(l);
3733
                }
3734
                var img = button.options.image;
3735
                if (img.indexOf('a_pixel') != -1) {
3736
                    img = '';
3737
                }
3738
                this.setImage(img);
3739
                if (this.options.imageClass && this.domImg) {
3740
                    this.domImg.removeClass(this.options.imageClass);
3741
                }
3742
                if (button.options.imageClass && this.domImg) {
3743
                    this.options.imageClass = button.options.imageClass;
3744
                    this.domImg.addClass(button.options.imageClass);
3745
                }
3746
                this.fireEvent('change', this);
3747
            }).bind(this)
3748
        });
3749
        if (this.options.items) {
3750
            this.add(this.options.items);
3751
        }
3752
        this.setEnabled(this.options.enabled);
3753
    },
3754
    
3755
    /**
3756
     * Method: setEnabled
3757
     * enable or disable the combo button.
3758
     *
3759
     * Parameters:
3760
     * enabled - {Boolean} the new enabled state of the button
3761
     */
3762
    setEnabled: function(enabled) {
3763
        this.options.enabled = enabled;
3764
        if (this.options.enabled) {
3765
            this.domObj.removeClass('jxDisabled');
3766
            if (this.domInput) {
3767
                this.domInput.disabled = false;
3768
            }
3769
        } else {
3770
            this.domObj.addClass('jxDisabled');
3771
            if (this.domInput) {
3772
                this.domInput.disabled = true;
3773
            }
3774
        }
3775
    },
3776
    
3777
    /**
3778
     * Method: valueChanged
3779
     * invoked when the current value is changed
3780
     */
3781
    valueChanged: function() {
3782
        this.fireEvent('change', this);
3783
    },
3784
    
3785
    /**
3786
     * Method: onKeyPress
3787
     * Handle the user pressing a key by looking for an ENTER key to set the
3788
     * value.
3789
     *
3790
     * Parameters:
3791
     * e - {Event} the keypress event
3792
     */
3793
    onKeyPress: function(e) {
3794
        if (e.key == 'enter') {
3795
            this.valueChanged();
3796
        }
3797
    },
3798
    
3799
    /**
3800
     * Method: add
3801
     * add a new item to the pick list
3802
     *
3803
     * Parameters:
3804
     * options - {Object} object with properties suitable to be passed to
3805
     * a <Jx.Menu.Item.Options> object.  More than one options object can be
3806
     * passed, comma separated or in an array.
3807
     */
3808
    add: function() {
3809
        $A(arguments).flatten().each(function(opt) {
3810
            var button = new Jx.Menu.Item($merge(opt,{
3811
                toggle: true
3812
            }));
3813
            this.menu.add(button);
3814
            this.buttonSet.add(button);
3815
        }, this);
3816
    },
3817
    
3818
    /**
3819
     * Method: remove
3820
     * Remove the item at the given index.  Not implemented.
3821
     *
3822
     * Parameters:
3823
     * idx - {Integer} the item to remove.
3824
     */
3825
    remove: function(idx) {
3826
        //TODO: implement remove?
3827
    },
3828
    
3829
    /**
3830
     * Method: setValue
3831
     * set the value of the Combo
3832
     *
3833
     * Parameters:
3834
     * value - {Object} the new value.  May be a string, a text node, or
3835
     * another DOM element.
3836
     */
3837
    setValue: function(value) {
3838
        if (this.options.editable) {
3839
            this.domInput.value = value;
3840
        } else {
3841
            this.setLabel(value);
3842
        }
3843
    },
3844
    
3845
    /**
3846
     * Method: getValue
3847
     * Return the current value
3848
     *
3849
     * Returns:
3850
     * {Object} returns the currently selected item
3851
     */
3852
    getValue: function() {
3853
        value = '';
3854
        if (this.options.editable) {
3855
            value = this.domInput.value;
3856
        } else {
3857
            value = this.getLabel();
3858
        }
3859
        return value;
3860
    }
3861
});// $Id: panel.js 428 2009-05-12 15:40:21Z kasi@arielgrafik.de $
3862
/**
3863
 * Class: Jx.Panel
3864
 *
3865
 * Extends: Object
3866
 *
3867
 * Implements: Options, Events, <Jx.ContentLoader>
3868
 *
3869
 * A panel is a fundamental container object that has a content
3870
 * area and optional toolbars around the content area.  It also
3871
 * has a title bar area that contains an optional label and
3872
 * some user controls as determined by the options passed to the
3873
 * constructor.
3874
 *
3875
 * Example:
3876
 * (code)
3877
 * (end)
3878
 *
3879
 * Events:
3880
 * close - fired when the panel is closed
3881
 * collapse - fired when the panel is collapsed
3882
 * expand - fired when the panel is opened
3883
 *
3884
 * License: 
3885
 * Copyright (c) 2008, DM Solutions Group Inc.
3886
 * 
3887
 * This file is licensed under an MIT style license
3888
 */
3889
Jx.Panel = new Class({
3890
    Family: 'Jx.Panel',
3891
    Implements: [Options, Events, Jx.ContentLoader, Jx.Addable],
3892
    
3893
    toolbarContainers: {
3894
        top: null,
3895
        right: null,
3896
        bottom: null,
3897
        left: null
3898
    },
3899
    
3900
     options: {
3901
        position: 'absolute',
3902
        type: 'Panel',
3903
        /* Option: id
3904
         * String, an id to assign to the panel's container
3905
         */
3906
        id: '',
3907
        /* Option: label
3908
         * String, the title of the Jx Panel
3909
         */
3910
        label: '&nbsp;',
3911
        /* Option: height
3912
         * integer, fixed height to give the panel - no fixed height by
3913
         * default.
3914
         */
3915
        height: null,
3916
        /* Option: collapse
3917
         * boolean, determine if the panel can be collapsed and expanded
3918
         * by the user.  This puts a control into the title bar for the user
3919
         * to control the state of the panel.
3920
         */
3921
        collapse: true,
3922
        /* Option: collapseTooltip
3923
         * the tooltip to display over the collapse button
3924
         */
3925
        collapseTooltip: 'Collapse/Expand Panel',
3926
        /* Option: collapseLabel
3927
         * the label to use for the collapse menu item
3928
         */
3929
        collapseLabel: 'Collapse',
3930
        /* Option: expandLabel
3931
         * the label to use for the expand menu item
3932
         */
3933
        expandLabel: 'Expand',
3934
        /* Option: maximizeTooltip
3935
         * the tooltip to display over the maximize button
3936
         */
3937
        maximizeTooltip: 'Maximize Panel',
3938
        /* Option: maximizeLabel
3939
         * the label to use for the maximize menu item
3940
         */
3941
        maximizeLabel: 'Maximize',
3942
        /* Option: close
3943
         * boolean, determine if the panel can be closed (hidden) by the user.
3944
         * The application needs to provide a way to re-open the panel after
3945
         * it is closed.  The closeable property extends to dialogs created by
3946
         * floating panels.  This option puts a control in the title bar of
3947
         * the panel.
3948
         */
3949
        close: false,
3950
        /* Option: closeTooltip
3951
         * the tooltip to display over the close button
3952
         */
3953
        closeTooltip: 'Close Panel',
3954
        /* Option: closeLabel
3955
         * the label to use for the close menu item
3956
         */
3957
        closeLabel: 'Close',
3958
        /* Option: closed
3959
         * boolean, initial state of the panel (true to start the panel
3960
         *  closed), default is false
3961
         */
3962
        closed: false,
3963
        /* Option: hideTitle
3964
         * Boolean, hide the title bar if true.  False by default.
3965
         */
3966
        hideTitle: false,
3967
        /* Option: toolbars
3968
         * array of Jx.Toolbar objects to put in the panel.  The position
3969
         * of each toolbar is used to position the toolbar within the panel.
3970
         */
3971
        toolbars: []
3972
    },
3973
    
3974
    /** 
3975
     * Constructor: Jx.Panel
3976
     * Initialize a new Jx.Panel instance
3977
     *
3978
     * Options: <Jx.Panel.Options>, <Jx.ContentLoader.Options>
3979
     */
3980
    initialize : function(options){
3981
        this.setOptions(options);
3982
        this.toolbars = options ? options.toolbars || [] : [];
3983
        
3984
        if ($defined(this.options.height) && !$defined(options.position)) {
3985
            this.options.position = 'relative';
3986
        }
3987

    
3988
        /* set up the title object */
3989
        this.title = new Element('div', {
3990
            'class': 'jx'+this.options.type+'Title'
3991
        });
3992
        
3993
        var i = new Element('img', {
3994
            'class': 'jx'+this.options.type+'Icon',
3995
            src: Jx.aPixel.src,
3996
            alt: '',
3997
            title: ''
3998
        });
3999
        if (this.options.image) {
4000
            i.setStyle('backgroundImage', 'url('+this.options.image+')');
4001
        }
4002
        this.title.adopt(i);
4003
        
4004
        this.labelObj = new Element('span', {
4005
            'class': 'jx'+this.options.type+'Label',
4006
            html: this.options.label
4007
        });
4008
        this.title.adopt(this.labelObj);
4009
        
4010
        var controls = new Element('div', {
4011
            'class': 'jx'+this.options.type+'Controls'
4012
        });
4013
        var tbDiv = new Element('div');
4014
        controls.adopt(tbDiv);
4015
        this.toolbar = new Jx.Toolbar({parent:tbDiv});
4016
        this.title.adopt(controls);
4017
        
4018
        var that = this;
4019
        
4020
        if (this.options.menu) {
4021
            this.menu = new Jx.Menu({
4022
                image: Jx.aPixel.src
4023
            });
4024
            this.menu.domObj.addClass('jx'+this.options.type+'Menu');
4025
            this.menu.domObj.addClass('jxButtonContentLeft');
4026
            this.toolbar.add(this.menu);
4027
        }
4028
        
4029
        if (this.options.collapse) {
4030
            var b = new Jx.Button({
4031
                image: Jx.aPixel.src,
4032
                tooltip: this.options.collapseTooltip,
4033
                onClick: function() {
4034
                    that.toggleCollapse();
4035
                }
4036
            });
4037
            b.domObj.addClass('jx'+this.options.type+'Collapse');
4038
            this.toolbar.add(b);
4039
            if (this.menu) {
4040
                var item = new Jx.Menu.Item({
4041
                    label: this.options.collapseLabel,
4042
                    onClick: function() { that.toggleCollapse(); }
4043
                });
4044
                this.addEvents({
4045
                    collapse: function() {
4046
                        item.setLabel(this.options.expandLabel);
4047
                    },
4048
                    expand: function() {
4049
                        item.setLabel(this.options.collapseLabel);
4050
                    }
4051
                });
4052
                this.menu.add(item);
4053
            }
4054
        }
4055
        
4056
        if (this.options.maximize) {
4057
            var b = new Jx.Button({
4058
                image: Jx.aPixel.src,
4059
                tooltip: this.options.maximizeTooltip,
4060
                onClick: function() {
4061
                    that.maximize();
4062
                }
4063
            });
4064
            b.domObj.addClass('jx'+this.options.type+'Maximize');
4065
            this.toolbar.add(b);
4066
            if (this.menu) {
4067
                var item = new Jx.Menu.Item({
4068
                    label: this.options.maximizeLabel,
4069
                    onClick: function() { that.maximize(); }
4070
                });
4071
                this.menu.add(item);
4072
            }
4073
        }
4074
        
4075
        if (this.options.close) {
4076
            var b = new Jx.Button({
4077
                image: Jx.aPixel.src,
4078
                tooltip: this.options.closeTooltip,
4079
                onClick: function() {
4080
                    that.close();
4081
                }
4082
            });
4083
            b.domObj.addClass('jx'+this.options.type+'Close');
4084
            this.toolbar.add(b);
4085
            if (this.menu) {
4086
                var item = new Jx.Menu.Item({
4087
                    label: this.options.closeLabel,
4088
                    onClick: function() {
4089
                        that.close();
4090
                    }
4091
                });
4092
                this.menu.add(item);
4093
            }
4094
            
4095
        }
4096
        
4097
        this.title.addEvent('dblclick', function() {
4098
            that.toggleCollapse();
4099
        });
4100
        
4101
        this.domObj = new Element('div', {
4102
            'class': 'jx'+this.options.type
4103
        });
4104
        if (this.options.id) {
4105
            this.domObj.id = this.options.id;
4106
        }
4107
        var jxl = new Jx.Layout(this.domObj, $merge(this.options, {propagate:false}));
4108
        var layoutHandler = this.layoutContent.bind(this);
4109
        jxl.addEvent('sizeChange', layoutHandler);
4110
        
4111
        if (!this.options.hideTitle) {
4112
            this.domObj.adopt(this.title);
4113
        }
4114
        
4115
        this.contentContainer = new Element('div', {
4116
            'class': 'jx'+this.options.type+'ContentContainer'
4117
        });
4118
        this.domObj.adopt(this.contentContainer);
4119
        
4120
        if ($type(this.options.toolbars) == 'array') {
4121
            this.options.toolbars.each(function(tb){
4122
                var position = tb.options.position;
4123
                var tbc = this.toolbarContainers[position];
4124
                if (!tbc) {
4125
                    var tbc = new Element('div');
4126
                    new Jx.Layout(tbc);
4127
                    this.contentContainer.adopt(tbc);
4128
                    this.toolbarContainers[position] = tbc;
4129
                }
4130
                tb.addTo(tbc);
4131
            }, this);
4132
        }
4133
        
4134
        this.content = new Element('div', {
4135
            'class': 'jx'+this.options.type+'Content'
4136
        });
4137
        
4138
        this.contentContainer.adopt(this.content);
4139
        new Jx.Layout(this.contentContainer);
4140
        new Jx.Layout(this.content);
4141
        
4142
        this.loadContent(this.content);
4143

    
4144
        this.toggleCollapse(this.options.closed);
4145
        
4146
        this.addEvent('addTo', function() {
4147
            this.domObj.resize();
4148
        });
4149
        if (this.options.parent) {
4150
            this.addTo(this.options.parent);
4151
        }
4152
    },
4153
    
4154
    /**
4155
     * Method: layoutContent
4156
     * the sizeChange event of the <Jx.Layout> that manages the outer container
4157
     * is intercepted and passed through this method to handle resizing of the
4158
     * panel contents because we need to do some calculations if the panel
4159
     * is collapsed and if there are toolbars to put around the content area.
4160
     */
4161
    layoutContent: function() {
4162
        var titleHeight = 0;
4163
        var top = 0;
4164
        var bottom = 0;
4165
        var left = 0;
4166
        var right = 0;
4167
        var tbc;
4168
        var tb;
4169
        var position;
4170
        if (!this.options.hideTitle && this.title.parentNode == this.domObj) {
4171
            titleHeight = this.title.getMarginBoxSize().height;
4172
        }
4173
        var domSize = this.domObj.getContentBoxSize();
4174
        if (domSize.height > titleHeight) {
4175
            this.contentContainer.setStyle('display','block');
4176
            this.options.closed = false;
4177
            this.contentContainer.resize({
4178
                top: titleHeight, 
4179
                height: null, 
4180
                bottom: 0
4181
            });
4182
            ['left','right'].each(function(position){
4183
                if (this.toolbarContainers[position]) {
4184
                    this.toolbarContainers[position].style.width = 'auto';
4185
                }
4186
            }, this);
4187
            ['top','bottom'].each(function(position){
4188
                if (this.toolbarContainers[position]) {
4189
                    this.toolbarContainers[position].style.height = '';                
4190
                }
4191
            }, this);
4192
            if ($type(this.options.toolbars) == 'array') {
4193
                this.options.toolbars.each(function(tb){
4194
                    position = tb.options.position;
4195
                    tbc = this.toolbarContainers[position];
4196
                    // IE 6 doesn't seem to want to measure the width of 
4197
                    // things correctly
4198
                    if (Browser.Engine.trident4) {
4199
                        var oldParent = $(tbc.parentNode);
4200
                        tbc.style.visibility = 'hidden';
4201
                        $(document.body).adopt(tbc);                    
4202
                    }
4203
                    var size = tbc.getBorderBoxSize();
4204
                    // put it back into its real parent now we are done 
4205
                    // measuring
4206
                    if (Browser.Engine.trident4) {
4207
                        oldParent.adopt(tbc);
4208
                        tbc.style.visibility = '';
4209
                    }
4210
                    switch(position) {
4211
                        case 'top':
4212
                            top = size.height;
4213
                            break;
4214
                        case 'bottom':
4215
                            bottom = size.height;
4216
                            break;
4217
                        case 'left':
4218
                            left = size.width;
4219
                            break;
4220
                        case 'right':
4221
                            right = size.width;
4222
                            break;
4223
                    }                    
4224
                },this);
4225
            }
4226
            tbc = this.toolbarContainers['top'];
4227
            if (tbc) {
4228
                tbc.resize({top: 0, left: left, right: right, bottom: null, height: top, width: null});
4229
            }
4230
            tbc = this.toolbarContainers['bottom'];
4231
            if (tbc) {
4232
                tbc.resize({top: null, left: left, right: right, bottom: 0, height: bottom, width: null});
4233
            }
4234
            tbc = this.toolbarContainers['left'];
4235
            if (tbc) {
4236
                tbc.resize({top: top, left: 0, right: null, bottom: bottom, height: null, width: left});
4237
            }
4238
            tbc = this.toolbarContainers['right'];
4239
            if (tbc) {
4240
                tbc.resize({top: top, left: null, right: 0, bottom: bottom, height: null, width: right});
4241
            }
4242
            this.content.resize({top: top, bottom: bottom, left: left, right: right});
4243
        } else {
4244
            this.contentContainer.setStyle('display','none');
4245
            this.options.closed = true;
4246
        }
4247
        this.fireEvent('sizeChange', this);
4248
    },
4249
    
4250
    /**
4251
     * Method: setLabel
4252
     * Set the label in the title bar of this panel
4253
     *
4254
     * Parameters:
4255
     * s - {String} the new label
4256
     */
4257
    setLabel: function(s) {
4258
        this.labelObj.innerHTML = s;
4259
    },
4260
    /**
4261
     * Method: getLabel
4262
     * Get the label of the title bar of this panel
4263
     *
4264
     * Returns: 
4265
     * {String} the label
4266
     */
4267
    getLabel: function() {
4268
        return this.labelObj.innerHTML;
4269
    },
4270
    /**
4271
     * Method: finalize
4272
     * Clean up the panel
4273
     */
4274
    finalize: function() {
4275
        this.domObj = null;
4276
        this.deregisterIds();
4277
    },
4278
    /**
4279
     * Method: maximize
4280
     * Maximize this panel
4281
     */
4282
    maximize: function() {
4283
        if (this.manager) {
4284
            this.manager.maximizePanel(this);
4285
        }
4286
    },
4287
    /**
4288
     * Method: setContent
4289
     * set the content of this panel to some HTML
4290
     *
4291
     * Parameters:
4292
     * html - {String} the new HTML to go in the panel
4293
     */
4294
    setContent : function (html) {
4295
        this.content.innerHTML = html;
4296
        this.bContentReady = true;
4297
    },
4298
    /**
4299
     * Method: setContentURL
4300
     * Set the content of this panel to come from some URL.
4301
     *
4302
     * Parameters:
4303
     * url - {String} URL to some HTML content for this panel
4304
     */
4305
    setContentURL : function (url) {
4306
        this.bContentReady = false;
4307
        this.setBusy(true);
4308
        if (arguments[1]) {
4309
            this.onContentReady = arguments[1];
4310
        }
4311
        if (url.indexOf('?') == -1) {
4312
            url = url + '?';
4313
        }
4314
        var a = new Request({
4315
            url: url,
4316
            method: 'get',
4317
            evalScripts:true,
4318
            onSuccess:this.panelContentLoaded.bind(this),
4319
            requestHeaders: ['If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT']
4320
        }).send();
4321
    },
4322
    /**
4323
     * Method: panelContentLoaded
4324
     * When the content of the panel is loaded from a remote URL, this 
4325
     * method is called when the ajax request returns.
4326
     *
4327
     * Parameters:
4328
     * html - {String} the html return from xhr.onSuccess
4329
     */
4330
    panelContentLoaded: function(html) {
4331
        this.content.innerHTML = html;
4332
        this.bContentReady = true;
4333
        this.setBusy(false);
4334
        if (this.onContentReady) {
4335
            window.setTimeout(this.onContentReady.bind(this),1);
4336
        }
4337
    },
4338
    /**
4339
     * Method: setBusy
4340
     * Set the panel as busy or not busy, which displays a loading image
4341
     * in the title bar.
4342
     *
4343
     * Parameters:
4344
     * isBusy - {Boolean} the busy state
4345
     */
4346
    setBusy : function(isBusy) {
4347
        this.busyCount += isBusy?1:-1;
4348
        if (this.loadingObj){
4349
            this.loadingObj.img.style.visibility = (this.busyCount>0)?'visible':'hidden';
4350
        }
4351
    },
4352
    
4353
    /**
4354
     * Method: toggleCollapse
4355
     * sets or toggles the collapsed state of the panel.  If a
4356
     * new state is passed, it is used, otherwise the current
4357
     * state is toggled.    
4358
     *
4359
     * Parameters:
4360
     * state - optional, if passed then the state is used, 
4361
     * otherwise the state is toggled.
4362
     */
4363
    toggleCollapse: function(state) {
4364
        if ($defined(state)) {
4365
            this.options.closed = state;
4366
        } else {
4367
            this.options.closed = !this.options.closed;
4368
        }
4369
        if (this.options.closed) {
4370
            if (!this.domObj.hasClass('jx'+this.options.type+'Min')) {
4371
                this.domObj.addClass('jx'+this.options.type+'Min');
4372
                this.contentContainer.setStyle('display','none');
4373
                var margin = this.domObj.getMarginSize();
4374
                var height = margin.top + margin.bottom;
4375
                if (this.title.parentNode == this.domObj) {
4376
                    height += this.title.getMarginBoxSize().height;
4377
                }
4378
                this.domObj.resize({height: height});
4379
                this.fireEvent('collapse', this);
4380
            }
4381
        } else {
4382
            if (this.domObj.hasClass('jx'+this.options.type+'Min')) {
4383
                this.domObj.removeClass('jx'+this.options.type+'Min');
4384
                this.contentContainer.setStyle('display','block');
4385
                this.domObj.resize({height: this.options.height});            
4386
                this.fireEvent('expand', this);
4387
            }
4388
        }
4389
    },
4390
    
4391
    /**
4392
     * Method: close
4393
     * Closes the panel (completely hiding it).
4394
     */
4395
    close: function() {
4396
        this.domObj.dispose();
4397
        this.fireEvent('close', this);
4398
    }
4399
    
4400
});// $Id: dialog.js 431 2009-05-13 12:58:48Z kasi@arielgrafik.de $
4401
/**
4402
 * Class: Jx.Dialog
4403
 *
4404
 * Extends: <Jx.Panel>
4405
 *
4406
 * Implements: <Jx.AutoPosition>, <Jx.Chrome>
4407
 *
4408
 * A Jx.Dialog implements a floating dialog.  Dialogs represent a useful way
4409
 * to present users with certain information or application controls.
4410
 * Jx.Dialog is designed to provide the same types of features as traditional
4411
 * operating system dialog boxes, including:
4412
 *
4413
 * - dialogs may be modal (user must dismiss the dialog to continue) or 
4414
 * non-modal
4415
 *
4416
 * - dialogs are movable (user can drag the title bar to move the dialog
4417
 * around)
4418
 *
4419
 * - dialogs may be a fixed size or allow user resizing.
4420
 *
4421
 * Jx.Dialog uses <Jx.ContentLoader> to load content into the content area
4422
 * of the dialog.  Refer to the <Jx.ContentLoader> documentation for details
4423
 * on content options.
4424
 *
4425
 * Example:
4426
 * (code)
4427
 * var dialog = new Jx.Dialog();
4428
 * (end)
4429
 *
4430
 * Events:
4431
 * open - triggered when the dialog is opened
4432
 * close - triggered when the dialog is closed
4433
 * change - triggered when the value of an input in the dialog is changed
4434
 * resize - triggered when the dialog is resized
4435
 *
4436
 * Extends:
4437
 * Jx.Dialog extends <Jx.Panel>, please go there for more details.
4438
 *
4439
 * License: 
4440
 * Copyright (c) 2008, DM Solutions Group Inc.
4441
 * 
4442
 * This file is licensed under an MIT style license
4443
 */
4444
Jx.Dialog = new Class({
4445
    Family: 'Jx.Dialog',
4446
    Extends: Jx.Panel,
4447
    Implements: [Jx.AutoPosition, Jx.Chrome],
4448
    
4449
    /**
4450
     * Property: {HTMLElement} blanket
4451
     * modal dialogs prevent interaction with the rest of the application
4452
     * while they are open, this element is displayed just under the
4453
     * dialog to prevent the user from clicking anything.
4454
     */
4455
    blanket: null,
4456
    
4457
    options: {
4458
        /* Option: modal
4459
         * (optional) {Boolean} controls whether the dialog will be modal
4460
         * or not.  The default is to create modal dialogs.
4461
         */
4462
        modal: true,
4463
        /* just overrides default position of panel, don't document this */
4464
        position: 'absolute',
4465
        /* Option: width
4466
         * (optional) {Integer} the initial width in pixels of the dialog.
4467
         * The default value is 250 if not specified.
4468
         */
4469
        width: 250,
4470
        /* Option: height
4471
         * (optional) {Integer} the initial height in pixels of the 
4472
         * dialog. The default value is 250 if not specified.
4473
         */
4474
        height: 250,
4475
        /* Option: horizontal
4476
         * (optional) {String} the horizontal rule for positioning the
4477
         * dialog.  The default is 'center center' meaning the dialog will be
4478
         * centered on the page.  See {<Jx.AutoPosition>} for details.
4479
         */
4480
        horizontal: 'center center',
4481
        /* Option: vertical
4482
         * (optional) {String} the vertical rule for positioning the
4483
         * dialog.  The default is 'center center' meaning the dialog will be
4484
         * centered on the page.  See {<Jx.AutoPosition>} for details.
4485
         */
4486
        vertical: 'center center',
4487
        /* Option: label
4488
         * (optional) {String} the title of the dialog box.  "New Dialog"
4489
         * is the default value.
4490
         */
4491
        label: 'New Dialog',
4492
        /* Option: id
4493
         * (optional) {String} an HTML ID to assign to the dialog, primarily
4494
         * used for applying CSS styles to specific dialogs
4495
         */
4496
        id: '',
4497
        /* Option: parent
4498
         * (optional) {HTMLElement} a reference to an HTML element that
4499
         * the dialog is to be contained by.  The default value is for the dialog
4500
         * to be contained by the body element.
4501
         */
4502
        parent: null,
4503
        /* Option: resize
4504
         * (optional) {Boolean} determines whether the dialog is
4505
         * resizeable by the user or not.  Default is false.
4506
         */
4507
        resize: false,
4508
        /* Option: resizeTooltip
4509
         * the tooltip to display for the resize handle, empty by default.
4510
         */
4511
        resizeTooltip: '',
4512
        /* Option: move
4513
         * (optional) {Boolean} determines whether the dialog is
4514
         * moveable by the user or not.  Default is true.
4515
         */
4516
        move: true,
4517
        /* Option: close
4518
         * (optional) {Boolean} determines whether the dialog is
4519
         * closeable by the user or not.  Default is true.
4520
         */
4521
        close: true
4522
    },
4523
    /**
4524
     * Constructor: Jx.Dialog
4525
     * Construct a new instance of Jx.Dialog
4526
     *
4527
     * Parameters: 
4528
     * options - {Object} an object containing options for the dialog.
4529
     *
4530
     * Options: <Jx.Dialog.Options>, <Jx.Panel.Options>, <Jx.ContentLoader.Options>
4531
     */
4532
    initialize: function(options) {
4533
        this.isOpening = false;
4534
        this.firstShow = true;
4535
        
4536
        /* initialize the panel overriding the type and position */
4537
        this.parent($merge(
4538
            {parent:document.body}, // these are defaults that can be overridden
4539
            options,
4540
            {type:'Dialog', position: 'absolute'} // these override anything passed to the options
4541
        ));
4542
        
4543
        this.options.parent = $(this.options.parent);
4544
        
4545
        if (this.options.modal) {
4546
            this.blanket = new Element('div',{
4547
                'class':'jxDialogModal',
4548
                styles:{
4549
                    display:'none',
4550
                    zIndex: -1
4551
                }
4552
            });
4553
            this.blanket.resize = (function() {
4554
                var ss = $(document.body).getScrollSize();
4555
                this.setStyles({
4556
                    width: ss.x,
4557
                    height: ss.y
4558
                });
4559
            }).bind(this.blanket);
4560
            this.options.parent.adopt(this.blanket);
4561
            window.addEvent('resize', this.blanket.resize);
4562
            
4563
        }
4564

    
4565
        this.domObj.setStyle('display','none');
4566
        this.options.parent.adopt(this.domObj);
4567
        
4568
        /* the dialog is moveable by its title bar */
4569
        if (this.options.move && typeof Drag != 'undefined') {
4570
            this.title.addClass('jxDialogMoveable');
4571
            new Drag(this.domObj, {
4572
                handle: this.title,
4573
                onBeforeStart: (function(){
4574
                    Jx.Dialog.orderDialogs(this);
4575
                }).bind(this),
4576
                onStart: (function() {
4577
                    this.contentContainer.setStyle('visibility','hidden');
4578
                    this.chrome.addClass('jxChromeDrag');
4579
                }).bind(this),
4580
                onComplete: (function() {
4581
                    this.chrome.removeClass('jxChromeDrag');
4582
                    this.contentContainer.setStyle('visibility','');
4583
                    var left = Math.max(this.chromeOffsets.left, parseInt(this.domObj.style.left,10));
4584
                    var top = Math.max(this.chromeOffsets.top, parseInt(this.domObj.style.top,10));
4585
                    this.options.horizontal = left + ' left';
4586
                    this.options.vertical = top + ' top';
4587
                    this.position(this.domObj, this.options.parent, this.options);
4588
                    this.options.left = parseInt(this.domObj.style.left,10);
4589
                    this.options.top = parseInt(this.domObj.style.top,10);
4590
                    if (!this.options.closed) {
4591
                        this.domObj.resize(this.options);                        
4592
                    }
4593
                }).bind(this)
4594
            });            
4595
        }
4596
        
4597
        /* the dialog is resizeable */
4598
        if (this.options.resize && typeof Drag != 'undefined') {
4599
            this.resizeHandle = new Element('div', {
4600
                'class':'jxDialogResize',
4601
                title: this.options.resizeTooltip,
4602
                styles: {
4603
                    'display':this.options.closed?'none':'block'
4604
                }
4605
            });
4606
            this.domObj.appendChild(this.resizeHandle);
4607

    
4608
            this.resizeHandleSize = this.resizeHandle.getSize(); 
4609
            this.resizeHandle.setStyles({
4610
                bottom: this.resizeHandleSize.height,
4611
                right: this.resizeHandleSize.width
4612
            });
4613
            this.domObj.makeResizable({
4614
                handle:this.resizeHandle,
4615
                onStart: (function() {
4616
                    this.contentContainer.setStyle('visibility','hidden');
4617
                    this.chrome.addClass('jxChromeDrag');
4618
                }).bind(this),
4619
                onDrag: (function() {
4620
                    this.resizeChrome(this.domObj);
4621
                }).bind(this),
4622
                onComplete: (function() {
4623
                    this.chrome.removeClass('jxChromeDrag');
4624
                    var size = this.domObj.getMarginBoxSize();
4625
                    this.options.width = size.width;
4626
                    this.options.height = size.height;
4627
                    this.layoutContent();
4628
                    this.domObj.resize(this.options);
4629
                    this.contentContainer.setStyle('visibility','');
4630
                    this.fireEvent('resize');
4631
                    this.resizeChrome(this.domObj);
4632
                    
4633
                }).bind(this)
4634
            });
4635
        }
4636
        /* this adjusts the zIndex of the dialogs when activated */
4637
        this.domObj.addEvent('mousedown', (function(){
4638
            Jx.Dialog.orderDialogs(this);
4639
        }).bind(this));
4640
    },
4641
    
4642
    /**
4643
     * Method: resize
4644
     * resize the dialog.  This can be called when the dialog is closed
4645
     * or open.
4646
     *
4647
     * Parameters:
4648
     * width - the new width
4649
     * height - the new height
4650
     * autoPosition - boolean, false by default, if resizing an open dialog
4651
     * setting this to true will reposition it according to its position
4652
     * rules.
4653
     */
4654
    resize: function(width, height, autoPosition) {
4655
        this.options.width = width;
4656
        this.options.height = height;
4657
        if (this.domObj.getStyle('display') != 'none') {
4658
            this.layoutContent();
4659
            this.domObj.resize(this.options);
4660
            this.fireEvent('resize');
4661
            this.resizeChrome(this.domObj);
4662
            if (autoPosition) {
4663
                this.position(this.domObj, this.options.parent, this.options);                
4664
            }
4665
        } else {
4666
            this.firstShow = false;
4667
        }
4668
    },
4669
    
4670
    /**
4671
     * Method: sizeChanged
4672
     * overload panel's sizeChanged method
4673
     */
4674
    sizeChanged: function() {
4675
        if (!this.options.closed) {
4676
            this.layoutContent();
4677
        }
4678
    },
4679
    
4680
    /**
4681
     * Method: toggleCollapse
4682
     * sets or toggles the collapsed state of the panel.  If a
4683
     * new state is passed, it is used, otherwise the current
4684
     * state is toggled.    
4685
     *
4686
     * Parameters:
4687
     * state - optional, if passed then the state is used, 
4688
     * otherwise the state is toggled.
4689
     */
4690
    toggleCollapse: function(state) {
4691
        if ($defined(state)) {
4692
            this.options.closed = state;
4693
        } else {
4694
            this.options.closed = !this.options.closed;
4695
        }
4696
        if (this.options.closed) {
4697
            if (!this.domObj.hasClass('jx'+this.options.type+'Min')) {
4698
                this.domObj.addClass('jx'+this.options.type+'Min');
4699
            }
4700
            this.contentContainer.setStyle('display','none');
4701
            if (this.resizeHandle) {
4702
                this.resizeHandle.setStyle('display','none');
4703
            }
4704
        } else {
4705
            if (this.domObj.hasClass('jx'+this.options.type+'Min')) {
4706
                this.domObj.removeClass('jx'+this.options.type+'Min');
4707
            }
4708
            this.contentContainer.setStyle('display','block');
4709
            if (this.resizeHandle) {
4710
                this.resizeHandle.setStyle('display','block');
4711
            }
4712
        }
4713
        
4714
        if (this.options.closed) {
4715
            var margin = this.domObj.getMarginSize();
4716
            var size = this.title.getMarginBoxSize();
4717
            this.domObj.resize({height: margin.top + size.height + margin.bottom});
4718
            this.fireEvent('collapse');
4719
        } else {
4720
            this.domObj.resize(this.options);
4721
            this.fireEvent('expand');
4722
        }
4723
        this.showChrome(this.domObj);
4724
    },
4725
    
4726
    /**
4727
     * Method: show
4728
     * show the dialog, external code should use the <Jx.Dialog::open> method
4729
     * to make the dialog visible.
4730
     */
4731
    show : function( ) {
4732
        /* prepare the dialog for display */
4733
        this.domObj.setStyles({
4734
            'display': 'block',
4735
            'visibility': 'hidden'
4736
        });
4737
        
4738
        if (this.blanket) {
4739
            this.blanket.resize();            
4740
        }
4741

    
4742
        Jx.Dialog.orderDialogs(this);
4743
        
4744
        /* do the modal thing */
4745
        if (this.blanket) {
4746
            this.blanket.setStyles({
4747
                visibility: 'visible',
4748
                display: 'block'
4749
            });
4750
        }
4751
        
4752
        if (this.options.closed) {
4753
            var margin = this.domObj.getMarginSize();
4754
            var size = this.title.getMarginBoxSize();
4755
            this.domObj.resize({height: margin.top + size.height + margin.bottom});
4756
        } else {
4757
            this.domObj.resize(this.options);            
4758
        }
4759
        if (this.firstShow) {
4760
            this.contentContainer.resize({forceResize: true});
4761
            this.layoutContent();
4762
            this.firstShow = false;
4763
            /* if the chrome got built before the first dialog show, it might
4764
             * not have been properly created and we should clear it so it
4765
             * does get built properly
4766
             */
4767
            if (this.chrome) {
4768
                this.chrome.dispose();
4769
                this.chrome = null;
4770
            }
4771
        }
4772
        /* update or create the chrome */
4773
        this.showChrome(this.domObj);
4774
        /* put it in the right place using auto-positioning */
4775
        this.position(this.domObj, this.options.parent, this.options);
4776
        this.domObj.setStyle('visibility', '');
4777
    },
4778
    /**
4779
     * Method: hide
4780
     * hide the dialog, external code should use the <Jx.Dialog::close>
4781
     * method to hide the dialog.
4782
     */
4783
    hide : function() {
4784
        Jx.Dialog.Stack.erase(this);
4785
        Jx.Dialog.ZIndex--;
4786
        this.domObj.setStyle('display','none');
4787
        if (this.blanket) {
4788
            this.blanket.setStyle('visibility', 'hidden');
4789
            Jx.Dialog.ZIndex--;
4790
        }
4791
        
4792
    },
4793
    /**
4794
     * Method: openURL
4795
     * open the dialog and load content from the provided url.  If you don't
4796
     * provide a URL then the dialog opens normally.
4797
     *
4798
     * Parameters:
4799
     * url - <String> the url to load when opening.
4800
     */
4801
    openURL: function(url) {
4802
        if (url) {
4803
            this.options.contentURL = url;
4804
            this.loadContent(this.content);
4805
        } else {
4806
            this.open();
4807
        }
4808
    },
4809
    
4810
    /**
4811
     * Method: open
4812
     * open the dialog.  This may be delayed depending on the 
4813
     * asynchronous loading of dialog content.  The onOpen
4814
     * callback function is called when the dialog actually
4815
     * opens
4816
     */
4817
    open: function() {
4818
        if (!this.isOpening) {
4819
            this.isOpening = true;
4820
        }
4821
        if (this.contentIsLoaded) {
4822
            this.show();
4823
            this.fireEvent('open', this);
4824
            this.isOpening = false;
4825
        } else {
4826
            this.addEvent('contentLoaded', this.open.bind(this));
4827
        }
4828
    },
4829
    /**
4830
     * Method: close
4831
     * close the dialog and trigger the onClose callback function
4832
     * if necessary
4833
     */
4834
    close: function() {
4835
        this.isOpening = false;
4836
        this.hide();
4837
        this.fireEvent('close');
4838
    }
4839
});
4840

    
4841
Jx.Dialog.Stack = [];
4842
Jx.Dialog.BaseZIndex = null;
4843
Jx.Dialog.orderDialogs = function(d) {
4844
    Jx.Dialog.Stack.erase(d).push(d);
4845
    if (Jx.Dialog.BaseZIndex === null) {
4846
        Jx.Dialog.BaseZIndex = Math.max(Jx.Dialog.Stack[0].domObj.getStyle('zIndex').toInt(), 1);
4847
    }
4848
    Jx.Dialog.Stack.each(function(d, i) {
4849
        var z = Jx.Dialog.BaseZIndex+i;
4850
        if (d.blanket) {
4851
            d.blanket.setStyle('zIndex',z);
4852
        }
4853
        d.domObj.setStyle('zIndex',z);
4854
    });
4855
    
4856
};
4857
// $Id: splitter.js 423 2009-05-12 12:37:56Z pagameba $
4858
/**
4859
 * Class: Jx.Splitter
4860
 *
4861
 * Extends: Object
4862
 *
4863
 * Implements: Options
4864
 *
4865
 * a Jx.Splitter creates two or more containers within a parent container
4866
 * and provides user control over the size of the containers.  The split
4867
 * can be made horizontally or vertically.
4868
 * 
4869
 * A horizontal split creates containers that divide the space horizontally
4870
 * with vertical bars between the containers.  A vertical split divides
4871
 * the space vertically and creates horizontal bars between the containers.
4872
 *
4873
 * Example:
4874
 * (code)
4875
 * (end)
4876
 *
4877
 * License: 
4878
 * Copyright (c) 2008, DM Solutions Group Inc.
4879
 * 
4880
 * This file is licensed under an MIT style license
4881
 */
4882
 
4883
Jx.Splitter = new Class({
4884
    Family: 'Jx.Splitter',
4885
    Implements: [Options],
4886
    /**
4887
     * Property: domObj
4888
     * {HTMLElement} the element being split
4889
     */
4890
    domObj: null,
4891
    /**
4892
     * Property: elements
4893
     * {Array} an array of elements that are displayed in each of the split 
4894
     * areas
4895
     */
4896
    elements: null,
4897
    /**
4898
     * Property: bars
4899
     * {Array} an array of the bars between each of the elements used to
4900
     * resize the split areas.
4901
     */
4902
    bars: null,
4903
    /**
4904
     * Property: firstUpdate
4905
     * {Boolean} track the first resize event so that unexposed Jx things
4906
     * can be forced to calculate their size the first time they are exposed.
4907
     */
4908
    firstUpdate: true,
4909
    options: {
4910
        /* Option: useChildren
4911
         * {Boolean} if set to true, then the children of the
4912
         * element to be split are used as the elements.  The default value is
4913
         * false.  If this is set, then the elements and splitInto options
4914
         * are ignored.
4915
         */
4916
        useChildren: false,
4917
        /* Option: splitInto
4918
         * {Integer} the number of elements to split the domObj into.
4919
         * If not set, then the length of the elements option is used, or 2 if
4920
         * elements is not specified.  If splitInto is specified and elements
4921
         * is specified, then splitInto is used.  If there are more elements than
4922
         * splitInto specifies, then the extras are ignored.  If there are less
4923
         * elements than splitInto specifies, then extras are created.
4924
         */
4925
        splitInto: 2,
4926
        /* Option: elements
4927
         * {Array} an array of elements to put into the split areas.
4928
         * If splitInto is not set, then it is calculated from the length of
4929
         * this array.
4930
         */
4931
        elements: null,
4932
        /* Option: containerOptions
4933
         * {Array} an array of objects that provide options
4934
         *  for the <Jx.Layout> constraints on each element.
4935
         */
4936
        containerOptions: [],
4937
        /* Option: barOptions
4938
         * {Array} an array of object that provide options for the bars,
4939
         * this array should be one less than the number of elements in the
4940
         * splitter.  The barOptions objects can contain a snap property indicating
4941
         * that a default snap object should be created in the bar and the value
4942
         * of 'before' or 'after' indicates which element it snaps open/shut.
4943
         */
4944
        barOptions: [],
4945
        /* Option: layout
4946
         * {String} either 'horizontal' or 'vertical', indicating the
4947
         * direction in which the domObj is to be split.
4948
         */
4949
        layout: 'horizontal',
4950
        /* Option: snaps
4951
         * {Array} an array of objects which can be used to snap
4952
         * elements open or closed.
4953
         */
4954
        snaps: [],
4955
        /* Option: barTooltip
4956
         * the tooltip to display when the mouse hovers over a split bar, 
4957
         * used for i18n.
4958
         */
4959
        barTooltip: 'drag this bar to resize',
4960
        /* Option: onStart
4961
         * an optional function to call when a bar starts dragging
4962
         */
4963
        onStart: null,
4964
        /* Option: onFinish
4965
         * an optional function to call when a bar finishes dragging
4966
         */
4967
        onFinish: null
4968
    },
4969
    /**
4970
     * Constructor: Jx.Splitter
4971
     * Create a new instance of Jx.Splitter
4972
     *
4973
     * Parameters:
4974
     * domObj - {HTMLElement} the element or id of the element to split
4975
     * options - <Jx.Splitter.Options>
4976
     */
4977
    initialize: function(domObj, options) {
4978
        this.setOptions(options);  
4979
        
4980
        this.domObj = $(domObj);
4981
        this.domObj.addClass('jxSplitContainer');
4982
        var jxLayout = this.domObj.retrieve('jxLayout');
4983
        if (jxLayout) {
4984
            jxLayout.addEvent('sizeChange', this.sizeChanged.bind(this));
4985
        }
4986
       
4987
        this.elements = [];
4988
        this.bars = [];
4989
        
4990
        var nSplits = 2;
4991
        if (this.options.useChildren) {
4992
            this.elements = this.domObj.getChildren();
4993
            nSplits = this.elements.length;
4994
        } else {
4995
            nSplits = this.options.elements ? 
4996
                            this.options.elements.length : 
4997
                            this.options.splitInto;
4998
            for (var i=0; i<nSplits; i++) {
4999
                var el;
5000
                if (this.options.elements && this.options.elements[i]) {
5001
                    if (options.elements[i].domObj) {
5002
                        el = options.elements[i].domObj;
5003
                    } else {
5004
                        el = $(this.options.elements[i]);                        
5005
                    }
5006
                    if (!el) {
5007
                        el = this.prepareElement();
5008
                        el.id = this.options.elements[i];
5009
                    }
5010
                } else {
5011
                    el = this.prepareElement();
5012
                }
5013
                this.elements[i] = el;
5014
                this.domObj.adopt(this.elements[i]);
5015
            }
5016
        }
5017
        this.elements.each(function(el) { el.addClass('jxSplitArea'); });
5018
        for (var i=0; i<nSplits; i++) {
5019
            var jxl = this.elements[i].retrieve('jxLayout');
5020
            if (!jxl) {
5021
                new Jx.Layout(this.elements[i], this.options.containerOptions[i]);
5022
            } else {
5023
                jxl.resize({position: 'absolute'});
5024
            }
5025
        }
5026
        
5027
        for (var i=1; i<nSplits; i++) {
5028
            var bar;
5029
            if (this.options.prepareBar) {
5030
                bar = this.options.prepareBar(i-1);                
5031
            } else {
5032
                bar = this.prepareBar();                
5033
            }
5034
            bar.store('splitterObj', this);
5035
            bar.store('leftSide',this.elements[i-1]);
5036
            bar.store('rightSide', this.elements[i]);
5037
            this.elements[i-1].store('rightBar', bar);
5038
            this.elements[i].store('leftBar', bar);
5039
            this.domObj.adopt(bar);
5040
            this.bars[i-1] = bar;
5041
        }
5042
        
5043
        //making dragging dependent on mootools Drag class
5044
        if ($defined(Drag)) {
5045
                this.establishConstraints();
5046
        }
5047
        
5048
        for (var i=0; i<this.options.barOptions.length; i++) {
5049
            if (!this.bars[i]) {
5050
                continue;
5051
            }
5052
            var opt = this.options.barOptions[i];
5053
            if (opt && opt.snap && (opt.snap == 'before' || opt.snap == 'after')) {
5054
                var element;
5055
                if (opt.snap == 'before') {
5056
                    element = this.bars[i].retrieve('leftSide');
5057
                } else if (opt.snap == 'after') {
5058
                    element = this.bars[i].retrieve('rightSide');
5059
                }
5060
                var snap;
5061
                var snapEvents;
5062
                if (opt.snapElement) {
5063
                    snap = opt.snapElement;
5064
                    snapEvents = opt.snapEvents || ['click', 'dblclick'];                    
5065
                } else {
5066
                    snap = this.bars[i];
5067
                    snapEvents = opt.snapEvents || ['dblclick'];
5068
                }
5069
                if (!snap.parentNode) {
5070
                    this.bars[i].adopt(snap);             
5071
                }
5072
                new Jx.Splitter.Snap(snap, element, this, snapEvents);
5073
            }
5074
        }
5075
        
5076
        for (var i=0; i<this.options.snaps.length; i++) {
5077
            if (this.options.snaps[i]) {
5078
                new Jx.Splitter.Snap(this.options.snaps[i], this.elements[i], this);
5079
            }
5080
        }
5081
        
5082
        this.sizeChanged();
5083
    },
5084
    /**
5085
     * Method: prepareElement
5086
     * Prepare a new, empty element to go into a split area.
5087
     *
5088
     * Returns:
5089
     * {HTMLElement} an HTMLElement that goes into a split area.
5090
     */
5091
    prepareElement: function(){
5092
        var o = new Element('div', {styles:{position:'absolute'}});
5093
        return o;
5094
    },
5095
    
5096
    /**
5097
     * Method: prepareBar
5098
     * Prepare a new, empty bar to go into between split areas.
5099
     *
5100
     * Returns:
5101
     * {HTMLElement} an HTMLElement that becomes a bar.
5102
     */
5103
    prepareBar: function() {
5104
        var o = new Element('div', {
5105
            'class': 'jxSplitBar'+this.options.layout.capitalize(),
5106
            'title': this.options.barTitle
5107
        });
5108
        return o;
5109
    },
5110
    
5111
    /**
5112
     * Method: establishConstraints
5113
     * Setup the initial set of constraints that set the behaviour of the
5114
     * bars between the elements in the split area.
5115
     */
5116
    establishConstraints: function() {
5117
        var modifiers = {x:null,y:null};
5118
        var fn;
5119
        if (this.options.layout == 'horizontal') {
5120
            modifiers.x = "left";
5121
            fn = this.dragHorizontal;
5122
        } else {
5123
            modifiers.y = "top";
5124
            fn = this.dragVertical;
5125
        }
5126
        if (typeof Drag != 'undefined') {
5127
            this.bars.each(function(bar){
5128
                var mask;
5129
                new Drag(bar, {
5130
                    //limit: limit,
5131
                    modifiers: modifiers,
5132
                    onSnap : function(obj) {
5133
                        obj.addClass('jxSplitBarDrag');
5134
                    },
5135
                    onComplete : (function(obj) {
5136
                        mask.destroy();
5137
                        obj.removeClass('jxSplitBarDrag');
5138
                        if (obj.retrieve('splitterObj') != this) {
5139
                            return;
5140
                        }
5141
                        fn.apply(this,[obj]);
5142
                    }).bind(this),
5143
                    onStart: (function(obj) {
5144
                        mask = new Element('div',{'class':'jxSplitterMask'}).inject(obj, 'after');
5145
                        if (this.options.onStart) {
5146
                            this.options.onStart();
5147
                        }
5148
                    }).bind(this),
5149
                    onFinish: (function() {
5150
                        if (this.options.onFinish) {
5151
                            this.options.onFinish();
5152
                        }
5153
                    }).bind(this)
5154
                });
5155
            }, this);            
5156
        }
5157
    },
5158
    
5159
    /**
5160
     * Method: dragHorizontal
5161
     * In a horizontally split container, handle a bar being dragged left or
5162
     * right by resizing the elements on either side of the bar.
5163
     *
5164
     * Parameters:
5165
     * obj - {HTMLElement} the bar that was dragged
5166
     */
5167
    dragHorizontal: function(obj) {
5168
        var leftEdge = parseInt(obj.style.left);
5169
        var leftSide = obj.retrieve('leftSide');
5170
        var rightSide = obj.retrieve('rightSide');
5171
        var leftJxl = leftSide.retrieve('jxLayout');
5172
        var rightJxl = rightSide.retrieve('jxLayout');
5173
        
5174
        var paddingLeft = this.domObj.getPaddingSize().left;
5175
        
5176
        /* process right side first */
5177
        var rsLeft, rsWidth, rsRight;
5178
        
5179
        var size = obj.retrieve('size');
5180
        if (!size) {
5181
            size = obj.getBorderBoxSize();
5182
            obj.store('size',size);
5183
        }
5184
        rsLeft = leftEdge + size.width - paddingLeft;
5185
        
5186
        var parentSize = this.domObj.getContentBoxSize();
5187
        
5188
        if (rightJxl.options.width != null) {
5189
            rsWidth = rightJxl.options.width + rightJxl.options.left - rsLeft;
5190
            rsRight = parentSize.width - rsLeft - rsWidth;
5191
        } else {
5192
            rsWidth = parentSize.width - rightJxl.options.right - rsLeft;
5193
            rsRight = rightJxl.options.right;
5194
        }
5195
        
5196
        /* enforce constraints on right side */
5197
        if (rsWidth < 0) {
5198
            rsWidth = 0;
5199
        }
5200
        
5201
        if (rsWidth < rightJxl.options.minWidth) {
5202
            rsWidth = rightJxl.options.minWidth;
5203
        }
5204
        if (rightJxl.options.maxWidth >= 0 && rsWidth > rightJxl.options.maxWidth) {
5205
            rsWidth = rightJxl.options.maxWidth;
5206
        }
5207
                
5208
        rsLeft = parentSize.width - rsRight - rsWidth;
5209
        leftEdge = rsLeft - size.width;
5210
        
5211
        /* process left side */
5212
        var lsLeft, lsWidth;
5213
        lsLeft = leftJxl.options.left;
5214
        lsWidth = leftEdge - lsLeft;
5215
        
5216
        /* enforce constraints on left */
5217
        if (lsWidth < 0) {
5218
            lsWidth = 0;
5219
        }
5220
        if (lsWidth < leftJxl.options.minWidth) {
5221
            lsWidth = leftJxl.options.minWidth;
5222
        }
5223
        if (leftJxl.options.maxWidth >= 0 && 
5224
            lsWidth > leftJxl.options.maxWidth) {
5225
            lsWidth = leftJxl.options.maxWidth;
5226
        }
5227
        
5228
        /* update the leftEdge to accomodate constraints */
5229
        if (lsLeft + lsWidth != leftEdge) {
5230
            /* need to update right side, ignoring constraints because left side
5231
               constraints take precedence (arbitrary decision)
5232
             */
5233
            leftEdge = lsLeft + lsWidth;
5234
            var delta = leftEdge + size.width - rsLeft;
5235
            rsLeft += delta;
5236
            rsWidth -= delta; 
5237
        }
5238
        
5239
        /* put bar in its final location based on constraints */
5240
        obj.style.left = paddingLeft + leftEdge + 'px';
5241
        
5242
        /* update leftSide positions */
5243
        if (leftJxl.options.width == null) {
5244
            var parentSize = this.domObj.getContentBoxSize();
5245
            leftSide.resize({right: parentSize.width - lsLeft-lsWidth});
5246
        } else {
5247
            leftSide.resize({width: lsWidth});
5248
        }
5249
        
5250
        /* update rightSide position */
5251
        if (rightJxl.options.width == null) {
5252
            rightSide.resize({left:rsLeft});
5253
        } else {
5254
            rightSide.resize({left: rsLeft, width: rsWidth});
5255
        }
5256
    },
5257
    
5258
    /**
5259
     * Method: dragVertical
5260
     * In a vertically split container, handle a bar being dragged up or
5261
     * down by resizing the elements on either side of the bar.
5262
     *
5263
     * Parameters:
5264
     * obj - {HTMLElement} the bar that was dragged
5265
     */
5266
    dragVertical: function(obj) {
5267
        /* top edge of the bar */
5268
        var topEdge = parseInt(obj.style.top);
5269
        
5270
        /* the containers on either side of the bar */
5271
        var topSide = obj.retrieve('leftSide');
5272
        var bottomSide = obj.retrieve('rightSide');
5273
        var topJxl = topSide.retrieve('jxLayout');
5274
        var bottomJxl = bottomSide.retrieve('jxLayout');
5275
        
5276
        var paddingTop = this.domObj.getPaddingSize().top;
5277
        
5278
        /* measure the bar and parent container for later use */
5279
        var size = obj.retrieve('size');
5280
        if (!size) {
5281
            size = obj.getBorderBoxSize();
5282
            obj.store('size', size);
5283
        }
5284
        var parentSize = this.domObj.getContentBoxSize();
5285

    
5286
        /* process top side first */
5287
        var bsTop, bsHeight, bsBottom;
5288
        
5289
        /* top edge of bottom side is the top edge of bar plus the height of the bar */
5290
        bsTop = topEdge + size.height - paddingTop;
5291
        
5292
        if (bottomJxl.options.height != null) {
5293
            /* bottom side height is fixed */
5294
            bsHeight = bottomJxl.options.height + bottomJxl.options.top - bsTop;
5295
            bsBottom = parentSize.height - bsTop - bsHeight;
5296
        } else {
5297
            /* bottom side height is not fixed. */
5298
            bsHeight = parentSize.height - bottomJxl.options.bottom - bsTop;
5299
            bsBottom = bottomJxl.options.bottom;
5300
        }
5301
        
5302
        /* enforce constraints on bottom side */
5303
        if (bsHeight < 0) {
5304
            bsHeight = 0;
5305
        }
5306
        
5307
        if (bsHeight < bottomJxl.options.minHeight) {
5308
            bsHeight = bottomJxl.options.minHeight;
5309
        }
5310
        
5311
        if (bottomJxl.options.maxHeight >= 0 && bsHeight > bottomJxl.options.maxHeight) {
5312
            bsHeight = bottomJxl.options.maxHeight;
5313
        }
5314
        
5315
        /* recalculate the top of the bottom side in case it changed
5316
           due to a constraint.  The bar may have moved also.
5317
         */
5318
        bsTop = parentSize.height - bsBottom - bsHeight;
5319
        topEdge = bsTop - size.height;
5320
                
5321
        /* process left side */
5322
        var tsTop, tsHeight;
5323
        tsTop = topJxl.options.top;
5324
        tsHeight = topEdge - tsTop;
5325
                        
5326
        /* enforce constraints on left */
5327
        if (tsHeight < 0) {
5328
            tsHeight = 0;
5329
        }
5330
        if (tsHeight < topJxl.options.minHeight) {
5331
            tsHeight = topJxl.options.minHeight;
5332
        }
5333
        if (topJxl.options.maxHeight >= 0 && 
5334
            tsHeight > topJxl.options.maxHeight) {
5335
            tsHeight = topJxl.options.maxHeight;
5336
        }
5337
        
5338
        /* update the topEdge to accomodate constraints */
5339
        if (tsTop + tsHeight != topEdge) {
5340
            /* need to update right side, ignoring constraints because left side
5341
               constraints take precedence (arbitrary decision)
5342
             */
5343
            topEdge = tsTop + tsHeight;
5344
            var delta = topEdge + size.height - bsTop;
5345
            bsTop += delta;
5346
            bsHeight -= delta; 
5347
        }
5348
        
5349
        /* put bar in its final location based on constraints */
5350
        obj.style.top = paddingTop + topEdge + 'px';
5351
        
5352
        /* update topSide positions */
5353
        if (topJxl.options.height == null) {
5354
            topSide.resize({bottom: parentSize.height - tsTop-tsHeight});
5355
        } else {
5356
            topSide.resize({height: tsHeight});
5357
        }
5358
        
5359
        /* update bottomSide position */
5360
        if (bottomJxl.options.height == null) {
5361
            bottomSide.resize({top:bsTop});
5362
        } else {
5363
            bottomSide.resize({top: bsTop, height: bsHeight});
5364
        }
5365
    },
5366
    
5367
    /**
5368
     * Method: sizeChanged
5369
     * handle the size of the container being changed.
5370
     */
5371
    sizeChanged: function() {
5372
        if (this.options.layout == 'horizontal') {
5373
            this.horizontalResize();
5374
        } else {
5375
            this.verticalResize();
5376
        }
5377
    },
5378
    
5379
    /**
5380
     * Method: horizontalResize
5381
     * Resize a horizontally layed-out container
5382
     */
5383
    horizontalResize: function() {
5384
        var availableSpace = this.domObj.getContentBoxSize().width;
5385
        var overallWidth = availableSpace;
5386

    
5387
        for (var i=0; i<this.bars.length; i++) {
5388
            var bar = this.bars[i];
5389
            var size = bar.retrieve('size');
5390
            if (!size || size.width == 0) {
5391
                size = bar.getBorderBoxSize();
5392
                bar.store('size',size);
5393
            }
5394
            availableSpace -= size.width;
5395
        }
5396

    
5397
        var nVariable = 0;
5398
        var jxo;
5399
        for (var i=0; i<this.elements.length; i++) {
5400
            var e = this.elements[i];
5401
            jxo = e.retrieve('jxLayout').options;
5402
            if (jxo.width != null) {
5403
                availableSpace -= parseInt(jxo.width);
5404
            } else {
5405
                var w = 0;
5406
                if (jxo.right != 0 || 
5407
                    jxo.left != 0) {
5408
                    w = e.getBorderBoxSize().width;
5409
                }
5410
                
5411
                availableSpace -= w;
5412
                nVariable++;
5413
            }
5414
        }
5415

    
5416
        if (nVariable == 0) { /* all fixed */
5417
            /* stick all available space in the last one */
5418
            availableSpace += jxo.width;
5419
            jxo.width = null;
5420
            nVariable = 1;
5421
        }
5422

    
5423
        var amount = parseInt(availableSpace / nVariable);
5424
        /* account for rounding errors */
5425
        var remainder = availableSpace % nVariable;
5426
        
5427
        var leftPadding = this.domObj.getPaddingSize().left;
5428

    
5429
        var currentPosition = 0;
5430

    
5431
        for (var i=0; i<this.elements.length; i++) {
5432
             var e = this.elements[i];
5433
             var jxl = e.retrieve('jxLayout');
5434
             var jxo = jxl.options;
5435
             if (jxo.width != null) {
5436
                 jxl.resize({left: currentPosition});
5437
                 currentPosition += jxo.width;
5438
             } else {
5439
                 var a = amount;
5440
                 if (nVariable == 1) {
5441
                     a += remainder;
5442
                 }
5443
                 nVariable--;
5444
                 
5445
                 var w = 0;
5446
                 if (jxo.right != 0 || jxo.left != 0) {
5447
                     w = e.getBorderBoxSize().width + a;
5448
                 } else {
5449
                     w = a;
5450
                 }
5451
                 
5452
                 if (w < 0) {
5453
                     if (nVariable > 0) {
5454
                         amount = amount + w/nVariable;
5455
                     }
5456
                     w = 0;
5457
                 }
5458
                 if (w < jxo.minWidth) {
5459
                     if (nVariable > 0) {
5460
                         amount = amount + (w - jxo.minWidth)/nVariable;
5461
                     }
5462
                     w = jxo.minWidth;
5463
                 }
5464
                 if (jxo.maxWidth >= 0 && w > jxo.maxWidth) {
5465
                     if (nVariable > 0) {
5466
                         amount = amount + (w - jxo.maxWidth)/nVariable;
5467
                     }
5468
                     w = e.options.maxWidth;
5469
                 }
5470
                 
5471
                 var r = overallWidth - currentPosition - w;
5472
                 jxl.resize({left: currentPosition, right: r});
5473
                 currentPosition += w;
5474
             }
5475
             var rightBar = e.retrieve('rightBar');
5476
             if (rightBar) {
5477
                 rightBar.setStyle('left', leftPadding + currentPosition);
5478
                 currentPosition += rightBar.retrieve('size').width;
5479
             }
5480
         }
5481
    },
5482
    
5483
    /**
5484
     * Method: verticalResize
5485
     * Resize a vertically layed out container.
5486
     */
5487
    verticalResize: function() { 
5488
        var availableSpace = this.domObj.getContentBoxSize().height;
5489
        var overallHeight = availableSpace;
5490

    
5491
        for (var i=0; i<this.bars.length; i++) {
5492
            var bar = this.bars[i];
5493
            var size = bar.retrieve('size');
5494
            if (!size || size.height == 0) {
5495
                size = bar.getBorderBoxSize();
5496
                bar.store('size', size);
5497
            }
5498
            availableSpace -= size.height;
5499
        }
5500

    
5501
        var nVariable = 0;
5502
        
5503
        var jxo;
5504
        for (var i=0; i<this.elements.length; i++) {
5505
            var e = this.elements[i];
5506
            jxo = e.retrieve('jxLayout').options;
5507
            if (jxo.height != null) {
5508
                availableSpace -= parseInt(jxo.height);
5509
            } else {
5510
                var h = 0;
5511
                if (jxo.bottom != 0 || jxo.top != 0) {
5512
                    h = e.getBorderBoxSize().height;
5513
                }
5514
                
5515
                availableSpace -= h;
5516
                nVariable++;
5517
            }
5518
        }
5519

    
5520
        if (nVariable == 0) { /* all fixed */
5521
            /* stick all available space in the last one */
5522
            availableSpace += jxo.height;
5523
            jxo.height = null;
5524
            nVariable = 1;
5525
        }
5526

    
5527
        var amount = parseInt(availableSpace / nVariable);
5528
        /* account for rounding errors */
5529
        var remainder = availableSpace % nVariable;
5530

    
5531
        var paddingTop = this.domObj.getPaddingSize().top;
5532
        
5533
        var currentPosition = 0;
5534

    
5535
        for (var i=0; i<this.elements.length; i++) {
5536
             var e = this.elements[i];
5537
             var jxl = e.retrieve('jxLayout');
5538
             var jxo = jxl.options;
5539
             if (jxo.height != null) {
5540
                 jxl.resize({top: currentPosition});
5541
                 currentPosition += jxo.height;
5542
             } else {
5543
                 var a = amount;
5544
                 if (nVariable == 1) {
5545
                     a += remainder;
5546
                 }
5547
                 nVariable--;
5548
                 
5549
                 var h = 0;
5550
                 if (jxo.bottom != 0 || jxo.top != 0) {
5551
                     h = e.getBorderBoxSize().height + a;
5552
                 } else {
5553
                     h = a;
5554
                 }
5555
                 
5556
                 if (h < 0) {
5557
                     if (nVariable > 0) {
5558
                         amount = amount + h/nVariable;
5559
                     }
5560
                     h = 0;
5561
                 }
5562
                 if (h < jxo.minHeight) {
5563
                     if (nVariable > 0) {
5564
                         amount = amount + (h - jxo.minHeight)/nVariable;
5565
                     }
5566
                     h = jxo.minHeight;
5567
                 }
5568
                 if (jxo.maxHeight >= 0 && h > jxo.maxHeight) {
5569
                     if (nVariable > 0) {
5570
                         amount = amount + (h - jxo.maxHeight)/nVariable;
5571
                     }
5572
                     h = jxo.maxHeight;
5573
                 }
5574
                 
5575
                 var r = overallHeight - currentPosition - h;
5576
                 jxl.resize({top: currentPosition, bottom: r});
5577
                 currentPosition += h;
5578
             }
5579
             var rightBar = e.retrieve('rightBar');
5580
             if (rightBar) {
5581
                 rightBar.style.top = paddingTop + currentPosition + 'px';
5582
                 currentPosition += rightBar.retrieve('size').height;
5583
             }
5584
         }
5585
    }
5586
});// $Id: panelset.js 423 2009-05-12 12:37:56Z pagameba $
5587
/**
5588
 * Class: Jx.PanelSet
5589
 *
5590
 * Extends: Object
5591
 *
5592
 * Implements: Options, Events, <Jx.Addable>
5593
 *
5594
 * A panel set manages a set of panels within a DOM element.  The PanelSet fills
5595
 * its container by resizing the panels in the set to fill the width and then
5596
 * distributing the height of the container across all the panels.  Panels
5597
 * can be resized by dragging their respective title bars to make them taller
5598
 * or shorter.  The maximize button on the panel title will cause all other
5599
 * panels to be closed and the target panel to be expanded to fill the remaining
5600
 * space.  In this respect, PanelSet works like a traditional Accordion control.
5601
 *
5602
 * When creating panels for use within a panel set, it is important to use the
5603
 * proper options.  You must override the collapse option and set it to false
5604
 * and add a maximize option set to true.  You must also not include options
5605
 * for menu and close.
5606
 *
5607
 * Example:
5608
 * (code)
5609
 * var p1 = new Jx.Panel({collapse: false, maximize: true, content: 'content1'});
5610
 * var p2 = new Jx.Panel({collapse: false, maximize: true, content: 'content2'});
5611
 * var p3 = new Jx.Panel({collapse: false, maximize: true, content: 'content3'});
5612
 * var panelSet = new Jx.PanelSet('panels', [p1,p2,p3]);
5613
 * (end)
5614
 *
5615
 * License: 
5616
 * Copyright (c) 2008, DM Solutions Group Inc.
5617
 * 
5618
 * This file is licensed under an MIT style license
5619
 */
5620
Jx.PanelSet = new Class({
5621
    Family: 'Jx.PanelSet',
5622
    Implements: [Options, Events, Jx.Addable],
5623
    
5624
    options: {
5625
        /* Option: parent
5626
         * the object to add the panel set to
5627
         */
5628
        parent: null,
5629
        /* Option: panels
5630
         * an array of <Jx.Panel> objects that will be managed by the set.
5631
         */
5632
        panels: [],
5633
        /* Option: barTooltip
5634
         * the tooltip to place on the title bars of each panel
5635
         */
5636
        barTooltip: 'drag this bar to resize'
5637
    },
5638
    
5639
    /**
5640
     * Property: panels
5641
     * {Array} the panels being managed by the set
5642
     */
5643
    panels: null,
5644
    /**
5645
     * Property: height
5646
     * {Integer} the height of the container, cached for speed
5647
     */
5648
    height: null,
5649
    /**
5650
     * Property: firstLayout
5651
     * {Boolean} true until the panel set has first been resized
5652
     */
5653
    firstLayout: true,
5654
    /**
5655
     * Constructor: Jx.PanelSet
5656
     * Create a new instance of Jx.PanelSet.
5657
     *
5658
     * Parameters:
5659
     * options - <Jx.PanelSet.Options>
5660
     *
5661
     * TODO: Jx.PanelSet.initialize
5662
     * Remove the panels parameter in favour of an add method.
5663
     */
5664
    initialize: function(options) {
5665
        if (options && options.panels) {
5666
            this.panels = options.panels;
5667
            options.panels = null;
5668
        }
5669
        this.setOptions(options);
5670
        this.domObj = new Element('div');
5671
        new Jx.Layout(this.domObj);
5672
        
5673
        //make a fake panel so we get the right number of splitters
5674
        var d = new Element('div', {styles:{position:'absolute'}});
5675
        new Jx.Layout(d, {minHeight:0,maxHeight:0,height:0});
5676
        var elements = [d];
5677
        this.panels.each(function(panel){
5678
            elements.push(panel.domObj);
5679
            panel.options.hideTitle = true;
5680
            panel.contentContainer.resize({top:0});
5681
            panel.toggleCollapse = this.maximizePanel.bind(this,panel);
5682
            panel.domObj.store('Jx.Panel', panel);
5683
            panel.manager = this;
5684
        }, this);
5685
        
5686
        this.splitter = new Jx.Splitter(this.domObj, {
5687
            splitInto: this.panels.length+1,
5688
            layout: 'vertical',
5689
            elements: elements,
5690
            prepareBar: (function(i) {
5691
                var bar = new Element('div', {
5692
                    'class': 'jxPanelBar',
5693
                    'title': this.options.barTooltip
5694
                });
5695
                
5696
                var panel = this.panels[i];
5697
                panel.title.setStyle('visibility', 'hidden');
5698
                $(document.body).adopt(panel.title);
5699
                var size = panel.title.getBorderBoxSize();
5700
                bar.adopt(panel.title);
5701
                panel.title.setStyle('visibility','');
5702
                
5703
                bar.setStyle('height', size.height);
5704
                bar.store('size', size);
5705
                
5706
                return bar;
5707
            }).bind(this)
5708
        });
5709
        this.addEvent('addTo', function() {
5710
            $(this.domObj.parentNode).setStyle('overflow', 'hidden');
5711
            this.domObj.resize();
5712
        });
5713
        if (this.options.parent) {
5714
            this.addTo(this.options.parent);
5715
        }
5716
    },
5717
    
5718
    /**
5719
     * Method: maximizePanel
5720
     * Maximize a panel, taking up all available space (taking into
5721
     * consideration any minimum or maximum values)
5722
     */
5723
    maximizePanel: function(panel) {
5724
        var domHeight = this.domObj.getContentBoxSize().height;
5725
        var space = domHeight;
5726
        var panelSize = panel.domObj.retrieve('jxLayout').options.maxHeight;
5727
        var panelIndex;
5728
        
5729
        /* calculate how much space might be left after setting all the panels to
5730
         * their minimum height (except the one we are resizing of course)
5731
         */
5732
        for (var i=1; i<this.splitter.elements.length; i++) {
5733
            var p = this.splitter.elements[i];
5734
            space -= p.retrieve('leftBar').getBorderBoxSize().height;
5735
            if (p !== panel.domObj) {
5736
                var thePanel = p.retrieve('Jx.Panel');
5737
                var o = p.retrieve('jxLayout').options;
5738
                space -= o.minHeight;
5739
            } else {
5740
                panelIndex = i;
5741
            }
5742
        }
5743

    
5744
        // calculate how much space the panel will take and what will be left over
5745
        if (panelSize == -1 || panelSize >= space) {
5746
            panelSize = space;
5747
            space = 0;
5748
        } else {
5749
            space = space - panelSize;
5750
        }
5751
        var top = 0;
5752
        for (var i=1; i<this.splitter.elements.length; i++) {
5753
            var p = this.splitter.elements[i];
5754
            top += p.retrieve('leftBar').getBorderBoxSize().height;
5755
            if (p !== panel.domObj) {
5756
                var thePanel = p.retrieve('Jx.Panel');
5757
                var o = p.retrieve('jxLayout').options;
5758
                var panelHeight = $chk(o.height) ? o.height : p.getBorderBoxSize().height;
5759
                if (space > 0) {
5760
                    if (space >= panelHeight) {
5761
                        // this panel can stay open at its current height
5762
                        space -= panelHeight;
5763
                        p.resize({top: top, height: panelHeight});
5764
                        top += panelHeight;
5765
                    } else {
5766
                        // this panel needs to shrink some
5767
                        if (space > o.minHeight) {
5768
                            // it can use all the space
5769
                            p.resize({top: top, height: space});
5770
                            top += space;
5771
                            space = 0;
5772
                        } else {
5773
                            p.resize({top: top, height: o.minHeight});
5774
                            top += o.minHeight;
5775
                        }
5776
                    }
5777
                } else {
5778
                    // no more space, just shrink away
5779
                    p.resize({top:top, height: o.minHeight});
5780
                    top += o.minHeight;
5781
                }
5782
                p.retrieve('rightBar').style.top = top + 'px';
5783
            } else {
5784
                break;
5785
            }
5786
        }
5787
        
5788
        /* now work from the bottom up */
5789
        var bottom = domHeight;
5790
        for (var i=this.splitter.elements.length - 1; i > 0; i--) {
5791
            p = this.splitter.elements[i];
5792
            if (p !== panel.domObj) {
5793
                var o = p.retrieve('jxLayout').options;
5794
                var panelHeight = $chk(o.height) ? o.height : p.getBorderBoxSize().height;
5795
                if (space > 0) {
5796
                    if (space >= panelHeight) {
5797
                        // panel can stay open
5798
                        bottom -= panelHeight;
5799
                        space -= panelHeight;
5800
                        p.resize({top: bottom, height: panelHeight});
5801
                    } else {
5802
                        if (space > o.minHeight) {
5803
                            bottom -= space;
5804
                            p.resize({top: bottom, height: space});
5805
                            space = 0;
5806
                        } else {
5807
                            bottom -= o.minHeight;
5808
                            p.resize({top: bottom, height: o.minHeight});
5809
                        }
5810
                    }
5811
                } else {
5812
                    bottom -= o.minHeight;
5813
                    p.resize({top: bottom, height: o.minHeight, bottom: null});                    
5814
                }
5815
                bottom -= p.retrieve('leftBar').getBorderBoxSize().height;
5816
                p.retrieve('leftBar').style.top = bottom + 'px';
5817
                
5818
            } else {
5819
                break;
5820
            }
5821
        }
5822
        panel.domObj.resize({top: top, height:panelSize, bottom: null});
5823
    }
5824
});// $Id: grid.js 423 2009-05-12 12:37:56Z pagameba $
5825
/**
5826
 * Class: Jx.Grid
5827
 * 
5828
 * Extends: Object
5829
 *
5830
 * Implements: Options, Events, <Jx.Addable>
5831
 *
5832
 * A tabular control that has fixed scrolling headers on the rows and columns
5833
 * like a spreadsheet.
5834
 *
5835
 * Jx.Grid is a tabular control with convenient controls for resizing columns,
5836
 * sorting, and inline editing.  It is created inside another element, typically a
5837
 * div.  If the div is resizable (for instance it fills the page or there is a
5838
 * user control allowing it to be resized), you must call the resize() method
5839
 * of the grid to let it know that its container has been resized.
5840
 *
5841
 * When creating a new Jx.Grid, you can specify a number of options for the grid
5842
 * that control its appearance and functionality.
5843
 *
5844
 * Jx.Grid renders data that comes from an external source.  This external 
5845
 * source, called the model, must implement the following interface.
5846
 *
5847
 *
5848
 * Example:
5849
 * (code)
5850
 * (end)
5851
 *
5852
 * License: 
5853
 * Copyright (c) 2008, DM Solutions Group Inc.
5854
 * 
5855
 * This file is licensed under an MIT style license
5856
 */
5857
Jx.Grid = new Class({
5858
    Family: 'Jx.Grid',
5859
    Implements: [Options, Events, Jx.Addable],
5860
    domObj : null,
5861
    model : null,
5862
    options: {
5863
         /* Option: parent
5864
          * the HTML element to create the grid inside. The grid will resize
5865
          * to fill the domObj.
5866
          */
5867
         parent: null,
5868
         /* Option: alternateRowColors
5869
          * defaults to false.  If set to true, then alternating CSS classes
5870
          * are used for rows.
5871
          */
5872
        alternateRowColors: false,
5873
         /* Option: rowHeaders
5874
          * defaults to false.  If set to true, then a column of row header
5875
          * cells are displayed.
5876
          */
5877
        rowHeaders: false,
5878
         /* Option: columnHeaders
5879
          * defaults to false.  If set to true, then a column of row header
5880
          * cells are displayed.
5881
          */
5882
        columnHeaders: false,
5883
         /* Option: rowSelection
5884
          * defaults to false.  If set to true, allow the user to select rows.
5885
          */
5886
        rowSelection: false,
5887
         /* Option: columnSelection
5888
          * defaults to false.  If set to true, allow the user to select
5889
          * columns.
5890
          */
5891
        columnSelection: false,
5892
         /* Option: cellPrelight
5893
          * defaults to false.  If set to true, the cell under the mouse is
5894
          * highlighted as the mouse moves.
5895
          */
5896
        cellPrelight: false,
5897
         /* Option: rowPrelight
5898
          * defaults to false.  If set to true, the row under the mouse is
5899
          * highlighted as the mouse moves.
5900
          */
5901
        rowPrelight: false,
5902
         /* Option: columnPrelight
5903
          * defaults to false.  If set to true, the column under the mouse is
5904
          * highlighted as the mouse moves.
5905
          */
5906
        columnPrelight: false,
5907
        /* Option: rowHeaderPrelight
5908
         * defaults to false.  If set to true, the row header of the row under
5909
         * the mouse is highlighted as the mouse moves.
5910
         */
5911
        rowHeaderPrelight: false,
5912
        /* Option: columnHeaderPrelight
5913
         * defaults to false.  If set to true, the column header of the column
5914
         * under the mouse is highlighted as the mouse moves.
5915
         */
5916
        columnHeaderPrelight: false,
5917
         /* Option: cellSelection
5918
          * defaults to false.  If set to true, allow the user to select
5919
          * cells.
5920
          */
5921
        cellSelection: false
5922
    },
5923
    /**
5924
     * Constructor: Jx.Grid
5925
     * construct a new instance of Jx.Grid within the domObj
5926
     *
5927
     * Parameters:
5928
     * options - <Jx.Grid.Options>
5929
     */
5930
    initialize : function( options ) {
5931
        this.setOptions(options);
5932

    
5933
        this.domObj = new Element('div');
5934
        new Jx.Layout(this.domObj, {
5935
            onSizeChange: this.resize.bind(this)
5936
        });
5937
        
5938
        if (this.options.parent) {
5939
            this.addTo(this.options.parent);
5940
        }        
5941
        
5942
        this.rowColObj = new Element('div', {'class':'jxGridContainer'});
5943
        
5944
        this.colObj = new Element('div', {'class':'jxGridContainer'});
5945
        this.colTable = new Element('table', {'class':'jxGridTable'});
5946
        this.colTableHead = new Element('thead');
5947
        this.colTable.appendChild(this.colTableHead);
5948
        this.colTableBody = new Element('tbody');
5949
        this.colTable.appendChild(this.colTableBody);
5950
        this.colObj.appendChild(this.colTable);
5951
        
5952
        this.rowObj = new Element('div', {'class':'jxGridContainer'});
5953
        this.rowTable = new Element('table', {'class':'jxGridTable'});
5954
        this.rowTableHead = new Element('thead');
5955
        this.rowTable.appendChild(this.rowTableHead);
5956
        this.rowObj.appendChild(this.rowTable);
5957
        
5958
        this.gridObj = new Element('div', {'class':'jxGridContainer',styles:{overflow:'scroll'}});
5959
        this.gridTable = new Element('table', {'class':'jxGridTable'});
5960
        this.gridTableBody = new Element('tbody');
5961
        this.gridTable.appendChild(this.gridTableBody);
5962
        this.gridObj.appendChild(this.gridTable);
5963
        
5964
        this.domObj.appendChild(this.rowColObj);
5965
        this.domObj.appendChild(this.rowObj);
5966
        this.domObj.appendChild(this.colObj);
5967
        this.domObj.appendChild(this.gridObj);
5968
                        
5969
        this.gridObj.addEvent('scroll', this.onScroll.bind(this));
5970
        this.gridObj.addEvent('click', this.onClickGrid.bindWithEvent(this));
5971
        this.rowObj.addEvent('click', this.onClickRowHeader.bindWithEvent(this));
5972
        this.colObj.addEvent('click', this.onClickColumnHeader.bindWithEvent(this));
5973
        this.gridObj.addEvent('mousemove', this.onMouseMoveGrid.bindWithEvent(this));
5974
        this.rowObj.addEvent('mousemove', this.onMouseMoveRowHeader.bindWithEvent(this));
5975
        this.colObj.addEvent('mousemove', this.onMouseMoveColumnHeader.bindWithEvent(this));
5976
    },
5977
    
5978
    /**
5979
     * Method: onScroll
5980
     * handle the grid scrolling by updating the position of the headers
5981
     */
5982
    onScroll: function() {
5983
        this.colObj.scrollLeft = this.gridObj.scrollLeft;
5984
        this.rowObj.scrollTop = this.gridObj.scrollTop;        
5985
    },
5986
    
5987
    /**
5988
     * Method: resize
5989
     * resize the grid to fit inside its container.  This involves knowing something
5990
     * about the model it is displaying (the height of the column header and the
5991
     * width of the row header) so nothing happens if no model is set
5992
     */
5993
    resize: function() {
5994
        if (!this.model) {
5995
            return;
5996
        }
5997
        
5998
        /* TODO: Jx.Grid.resize
5999
         * if not showing column or row, should we handle the resize differently
6000
         */
6001
        var colHeight = this.options.columnHeaders ? this.model.getColumnHeaderHeight() : 1;
6002
        var rowWidth = this.options.rowHeaders ? this.model.getRowHeaderWidth() : 1;
6003
        
6004
        var size = Element.getContentBoxSize(this.domObj);
6005
        
6006
        /* -1 because of the right/bottom borders */
6007
        this.rowColObj.setStyles({
6008
            width: rowWidth-1, 
6009
            height: colHeight-1
6010
        });
6011
        this.rowObj.setStyles({
6012
            top:colHeight,
6013
            left:0,
6014
            width:rowWidth-1,
6015
            height:size.height-colHeight-1
6016
        });
6017

    
6018
        this.colObj.setStyles({
6019
            top: 0,
6020
            left: rowWidth,
6021
            width: size.width - rowWidth - 1,
6022
            height: colHeight - 1
6023
        });
6024

    
6025
        this.gridObj.setStyles({
6026
            top: colHeight,
6027
            left: rowWidth,
6028
            width: size.width - rowWidth - 1,
6029
            height: size.height - colHeight - 1 
6030
        });
6031
    },
6032
    
6033
    /**
6034
     * Method: setModel
6035
     * set the model for the grid to display.  If a model is attached to the grid
6036
     * it is removed and the new model is displayed.
6037
     * 
6038
     * Parameters:
6039
     * model - {Object} the model to use for this grid
6040
     */
6041
    setModel: function(model) {
6042
        this.model = model;
6043
        if (this.model) {
6044
            if (this.domObj.resize) {
6045
                this.domObj.resize();
6046
            }
6047
            this.createGrid();
6048
            this.resize();
6049
        } else {
6050
            this.destroyGrid();
6051
        }
6052
    },
6053
    
6054
    /**
6055
     * Method: destroyGrid
6056
     * destroy the contents of the grid safely
6057
     */
6058
    destroyGrid: function() {
6059
        var n = this.colTableHead.cloneNode(false);
6060
        this.colTable.replaceChild(n, this.colTableHead);
6061
        this.colTableHead = n;
6062
        
6063
        n = this.colTableBody.cloneNode(false);
6064
        this.colTable.replaceChild(n, this.colTableBody);
6065
        this.colTableBody = n;
6066
        
6067
        n = this.rowTableHead.cloneNode(false);
6068
        this.rowTable.replaceChild(n, this.rowTableHead);
6069
        this.rowTableHead = n;
6070
        
6071
        n = this.gridTableBody.cloneNode(false);
6072
        this.gridTable.replaceChild(n, this.gridTableBody);
6073
        this.gridTableBody = n;
6074
        
6075
    },
6076
    
6077
    /**
6078
     * Method: createGrid
6079
     * create the grid for the current model
6080
     */
6081
    createGrid: function() {
6082
        this.destroyGrid();
6083
        if (this.model) {
6084
            var model = this.model;
6085
            var nColumns = model.getColumnCount();
6086
            var nRows = model.getRowCount();
6087
            
6088
            /* create header if necessary */
6089
            if (this.options.columnHeaders) {
6090
                var colHeight = model.getColumnHeaderHeight();
6091
                var trHead = new Element('tr');
6092
                this.colTableHead.appendChild(trHead);
6093
                var trBody = new Element('tr');
6094
                this.colTableBody.appendChild(trBody);
6095
                
6096
                var th = new Element('th', {styles:{width:0,height:0}});
6097
                trHead.appendChild(th);
6098
                th = th.cloneNode(true);
6099
                th.setStyle('height',colHeight);
6100
                trBody.appendChild(th);
6101
                for (var i=0; i<nColumns; i++) {
6102
                    var colWidth = model.getColumnWidth(i);
6103
                    th = new Element('th', {'class':'jxGridColHeadHide',styles:{width:colWidth}});
6104
                    var p = new Element('p', {styles:{height:0,width:colWidth}});
6105
                    th.appendChild(p);
6106
                    trHead.appendChild(th);
6107
                    th = new Element('th', {
6108
                        'class':'jxGridColHead', 
6109
                        html:model.getColumnHeaderHTML(i)
6110
                    });
6111
                    trBody.appendChild(th);
6112
                }
6113
                /* one extra column at the end for filler */
6114
                var th = new Element('th',{styles:{width:1000,height:0}});
6115
                trHead.appendChild(th);
6116
                th = th.cloneNode(true);
6117
                th.setStyle('height',colHeight - 1);
6118
                th.className = 'jxGridColHead';
6119
                trBody.appendChild(th);
6120
                
6121
            }
6122
            
6123
            if (this.options.rowHeaders) {
6124
                var rowWidth = model.getRowHeaderWidth();
6125
                var tr = new Element('tr');
6126
                var td = new Element('td', {styles:{width:0,height:0}});
6127
                tr.appendChild(td);
6128
                var th = new Element('th', {styles:{width:rowWidth,height:0}});
6129
                tr.appendChild(th);
6130
                this.rowTableHead.appendChild(tr);
6131
                for (var i=0; i<nRows; i++) {
6132
                    var rowHeight = model.getRowHeight(i);
6133
                    var tr = new Element('tr');
6134
                    var td = new Element('td', {'class':'jxGridRowHeadHide', styles:{width:0,height:rowHeight}});
6135
                    var p = new Element('p', {styles:{width:0,height:rowHeight}});
6136
                    td.appendChild(p);
6137
                    tr.appendChild(td);
6138
                    var th = new Element('th', {'class':'jxGridRowHead', html:model.getRowHeaderHTML(i)});
6139
                    tr.appendChild(th);
6140
                    this.rowTableHead.appendChild(tr);
6141
                }
6142
                /* one extra row at the end for filler */
6143
                var tr = new Element('tr');
6144
                var td = new Element('td',{
6145
                    styles:{
6146
                        width:0,
6147
                        height:1000
6148
                    }
6149
                });
6150
                tr.appendChild(td);
6151
                var th = new Element('th',{
6152
                    'class':'jxGridRowHead',
6153
                    styles:{
6154
                        width:rowWidth,
6155
                        height:1000
6156
                    }
6157
                });
6158
                tr.appendChild(th);
6159
                this.rowTableHead.appendChild(tr);
6160
            }
6161
            
6162
            var colHeight = model.getColumnHeaderHeight();
6163
            var trBody = new Element('tr');
6164
            this.gridTableBody.appendChild(trBody);
6165
            
6166
            var td = new Element('td', {styles:{width:0,height:0}});
6167
            trBody.appendChild(td);
6168
            for (var i=0; i<nColumns; i++) {
6169
                var colWidth = model.getColumnWidth(i);
6170
                td = new Element('td', {'class':'jxGridColHeadHide', styles:{width:colWidth}});
6171
                var p = new Element('p', {styles:{width:colWidth,height:0}});
6172
                td.appendChild(p);
6173
                trBody.appendChild(td);
6174
            }
6175
            
6176
            for (var j=0; j<nRows; j++) {
6177
                var rowHeight = model.getRowHeight(j);
6178
                var actualRowHeight = rowHeight;
6179
                var tr = new Element('tr');
6180
                this.gridTableBody.appendChild(tr);
6181
                
6182
                var td = new Element('td', {
6183
                    'class':'jxGridRowHeadHide',
6184
                    styles: {
6185
                        width: 0,
6186
                        height:rowHeight
6187
                    }
6188
                });
6189
                var p = new Element('p',{styles:{height:rowHeight}});
6190
                td.appendChild(p);
6191
                tr.appendChild(td);
6192
                for (var i=0; i<nColumns; i++) {
6193
                    var colWidth = model.getColumnWidth(i);
6194
                    td = new Element('td', {'class':'jxGridCell'});
6195
                    td.innerHTML = model.getValueAt(j,i);
6196
                    tr.appendChild(td);
6197
                    var tdSize = td.getSize();
6198
                    if (tdSize.height > actualRowHeight) {
6199
                        actualRowHeight = tdSize.height;
6200
                    }
6201
                }
6202
                /* some notes about row sizing
6203
                 * In Safari, the height of a TR is always returned as 0
6204
                 * In Safari, the height of any given TD is the height it would
6205
                 * render at, not the actual height of the row
6206
                 * In IE, the height is returned 1px bigger than any other browser
6207
                 * Firefox just works
6208
                 *
6209
                 * So, for Safari, we have to measure every TD and take the highest one
6210
                 * and if its IE, we subtract 1 from the overall height, making all
6211
                 * browsers identical
6212
                 *
6213
                 * Using document.all is not a good hack for this
6214
                 */
6215
                if (document.all) {
6216
                    actualRowHeight -= 1;
6217
                }
6218
                if (this.options.rowHeaders) {
6219
                    this.setRowHeaderHeight(j, actualRowHeight);                    
6220
                }
6221
                /* if we apply the class before adding content, it
6222
                 * causes a rendering error in IE (off by 1) that is 'fixed'
6223
                 * when another class is applied to the row, causing dynamic
6224
                 * shifting of the row heights
6225
                 */
6226
                if (this.options.alternateRowColors) {
6227
                    tr.className = (j%2) ? 'jxGridRowOdd' : 'jxGridRowEven';
6228
                } else {
6229
                    tr.className = 'jxGridRowAll';
6230
                }
6231
            }
6232
            
6233
        }
6234
    },
6235
    
6236
    /**
6237
     * Method: setRowHeaderHeight
6238
     * set the height of a row.  This is used internally to adjust the height of
6239
     * the row header when cell contents wrap.  A limitation of the table structure
6240
     * is that overflow: hidden on a td will work horizontally but not vertically
6241
     *
6242
     * Parameters:
6243
     * row - {Integer} the row to set the height for
6244
     * height - {Integer} the height to set the row (in pixels)
6245
     */
6246
    setRowHeaderHeight: function(row, height) {
6247
        //this.rowTableHead.childNodes[row+1].childNodes[0].style.height = (height) + 'px';
6248
        this.rowTableHead.childNodes[row+1].childNodes[0].childNodes[0].style.height = (height) + 'px';
6249
    },
6250
    
6251
    /**
6252
     * Method: gridChanged
6253
     * called through the grid listener interface when data has changed in the
6254
     * underlying model
6255
     *
6256
     * Parameters:
6257
     * model - {Object} the model that changed
6258
     * row - {Integer} the row that changed
6259
     * col - {Integer} the column that changed
6260
     * value - {Mixed} the new value
6261
     */
6262
    gridChanged: function(model, row, col, value) {
6263
        if (this.model == model) {
6264
            this.gridObj.childNodes[row].childNodes[col].innerHTML = value;
6265
        }
6266
    },
6267
    
6268
    /** 
6269
     * Method: prelightRowHeader
6270
     * apply the jxGridRowHeaderPrelight style to the header cell of a row.
6271
     * This removes the style from the previously pre-lit row header.
6272
     * 
6273
     * Parameters:
6274
     * row - {Integer} the row to pre-light the header cell of
6275
     */
6276
    prelightRowHeader: function(row) {
6277
        var cell = (row >= 0 && row < this.rowTableHead.rows.length-1) ? this.rowTableHead.rows[row+1].cells[1] : null;
6278
        if (this.prelitRowHeader != cell) {
6279
            if (this.prelitRowHeader) {
6280
                this.prelitRowHeader.removeClass('jxGridRowHeaderPrelight');
6281
            }
6282
            this.prelitRowHeader = cell;
6283
            if (this.prelitRowHeader) {
6284
                this.prelitRowHeader.addClass('jxGridRowHeaderPrelight');
6285
            }
6286
        }
6287
    },
6288
    
6289
    /** 
6290
     * Method: prelightColumnHeader
6291
     * apply the jxGridColumnHeaderPrelight style to the header cell of a column.
6292
     * This removes the style from the previously pre-lit column header.
6293
     * 
6294
     * Parameters:
6295
     * col - {Integer} the column to pre-light the header cell of
6296
     */
6297
    prelightColumnHeader: function(col) {
6298
        if (this.colTableBody.rows.length == 0) {
6299
            return;
6300
        }
6301
        var cell = (col >= 0 && col < this.colTableBody.rows[0].cells.length-1) ? this.colTableBody.rows[0].cells[col+1] : null;
6302
        if (this.prelitColumnHeader != cell) {
6303
            if (this.prelitColumnHeader) {
6304
                this.prelitColumnHeader.removeClass('jxGridColumnHeaderPrelight');
6305
            }
6306
            this.prelitColumnHeader = cell;
6307
            if (this.prelitColumnHeader) {
6308
                this.prelitColumnHeader.addClass('jxGridColumnHeaderPrelight');
6309
            }
6310
        }
6311
    },
6312
    
6313
    /** 
6314
     * Method: prelightRow
6315
     * apply the jxGridRowPrelight style to row.
6316
     * This removes the style from the previously pre-lit row.
6317
     * 
6318
     * Parameters:
6319
     * row - {Integer} the row to pre-light
6320
     */
6321
    prelightRow: function(row) {
6322
        var tr = (row >= 0 && row < this.gridTableBody.rows.length-1) ? this.gridTableBody.rows[row+1] : null;
6323
        
6324
        if (this.prelitRow != row) {
6325
            if (this.prelitRow) {
6326
                this.prelitRow.removeClass('jxGridRowPrelight');
6327
            }
6328
            this.prelitRow = tr;
6329
            if (this.prelitRow) {
6330
                this.prelightRowHeader(row);
6331
                this.prelitRow.addClass('jxGridRowPrelight');
6332
            }
6333
        }
6334
    },
6335
    
6336
    /** 
6337
     * Method: prelightColumn
6338
     * apply the jxGridColumnPrelight style to a column.
6339
     * This removes the style from the previously pre-lit column.
6340
     * 
6341
     * Parameters:
6342
     * col - {Integer} the column to pre-light
6343
     *
6344
     * TODO: Jx.Grid.prelightColumn
6345
     * Not Yet Implemented.
6346
     */
6347
    prelightColumn: function(col) {
6348
        /* TODO: Jx.Grid.prelightColumn
6349
         * implement column prelighting (possibly) 
6350
         */
6351
        if (col >= 0 && col < this.gridTable.rows[0].cells.length) {
6352
            if ($chk(this.prelitColumn)) {
6353
                for (var i=0; i<this.gridTable.rows.length; i++) {
6354
                    this.gridTable.rows[i].cells[this.prelitColumn + 1].removeClass('jxGridColumnPrelight');
6355
                }
6356
            }
6357
            this.prelitColumn = col;
6358
            for (var i=0; i<this.gridTable.rows.length; i++) {
6359
                this.gridTable.rows[i].cells[col + 1].addClass('jxGridColumnPrelight');
6360
            }            
6361
        }
6362
        
6363
        this.prelightColumnHeader(col);
6364
    },
6365
    
6366
    /** 
6367
     * Method: prelightCell
6368
     * apply the jxGridCellPrelight style to a cell.
6369
     * This removes the style from the previously pre-lit cell.
6370
     *
6371
     * Parameters:
6372
     * row - {Integer} the row of the cell to pre-light
6373
     * col - {Integer} the column of the cell to pre-light
6374
     */
6375
    prelightCell: function(row, col) {
6376
         var td = (row >=0 && col >=0 && row < this.gridTableBody.rows.length - 1 && col < this.gridTableBody.rows[row+1].cells.length - 1) ? this.gridTableBody.rows[row+1].cells[col+1] : null;
6377
        if (this.prelitCell != td) {
6378
            if (this.prelitCell) {
6379
                this.prelitCell.removeClass('jxGridCellPrelight');
6380
            }
6381
            this.prelitCell = td;
6382
            if (this.prelitCell) {
6383
                this.prelitCell.addClass('jxGridCellPrelight');
6384
            }
6385
        }    
6386
    },
6387
    
6388
    /** 
6389
     * Method: selectCell
6390
     * Select a cell and apply the jxGridCellSelected style to it.
6391
     * This deselects a previously selected cell.
6392
     *
6393
     * If the model supports cell selection, it should implement
6394
     * a cellSelected function to receive notification of the selection.
6395
     *
6396
     * Parameters:
6397
     * row - {Integer} the row of the cell to select
6398
     * col - {Integer} the column of the cell to select
6399
     */
6400
    selectCell: function(row, col) {
6401
         var td = (row >=0 && col >=0 && row < this.gridTableBody.rows.length - 1 && col < this.gridTableBody.rows[row+1].cells.length - 1) ? this.gridTableBody.rows[row+1].cells[col+1] : null;
6402
         if (!td) {
6403
             return;
6404
         }
6405
         
6406
         if (this.selectedCell) {
6407
             this.selectedCell.removeClass('jxGridCellSelected');
6408
         }
6409
         this.selectedCell = td;
6410
         this.selectedCell.addClass('jxGridCellSelected');
6411
    },
6412
    
6413
    /** 
6414
     * Method: selectRowHeader
6415
     * Apply the jxGridRowHeaderSelected style to the row header cell of a
6416
     * selected row.
6417
     *
6418
     * Parameters:
6419
     * row - {Integer} the row header to select
6420
     * selected - {Boolean} the new state of the row header
6421
     */
6422
    selectRowHeader: function(row, selected) {
6423
        var cell = (row >= 0 && row < this.rowTableHead.rows.length-1) ? this.rowTableHead.rows[row+1].cells[1] : null;
6424
        if (!cell) {
6425
            return;
6426
        }
6427
        if (selected) {
6428
            cell.addClass('jxGridRowHeaderSelected');
6429
        } else {
6430
            cell.removeClass('jxGridRowHeaderSelected');
6431
        }
6432
    },
6433
    
6434
    /** 
6435
     * Method: selectRow
6436
     * Select a row and apply the jxGridRowSelected style to it.
6437
     *
6438
     * If the model supports row selection, it should implement
6439
     * a rowSelected function to receive notification of the selection.
6440
     *
6441
     * Parameters:
6442
     * row - {Integer} the row to select
6443
     * selected - {Boolean} the new state of the row
6444
     */
6445
    selectRow: function(row, selected) {
6446
        var tr = (row >= 0 && row < this.gridTableBody.rows.length - 1) ? this.gridTableBody.rows[row+1] : null;
6447
        if (tr) {
6448
            if (selected) {
6449
                tr.addClass('jxGridRowSelected');
6450
            } else {
6451
                tr.removeClass('jxGridRowSelected');
6452
            }
6453
            this.selectRowHeader(row, selected);
6454
        }
6455
    },
6456
    
6457
    /** 
6458
     * method: selectColumnHeader
6459
     * Apply the jxGridColumnHeaderSelected style to the column header cell of a
6460
     * selected column.
6461
     *
6462
     * Parameters:
6463
     * col - {Integer} the column header to select
6464
     * selected - {Boolean} the new state of the column header
6465
     */
6466
    selectColumnHeader: function(col, selected) {
6467
        if (this.colTableBody.rows.length == 0) {
6468
            return;
6469
        }
6470
        var cell = (col >= 0 && col < this.colTableBody.rows[0].cells.length-1) ? this.colTableBody.rows[0].cells[col+1] : null;
6471
        if (cell == null) { 
6472
            return; 
6473
        }
6474
        
6475
        if (selected) {
6476
            cell.addClass('jxGridColumnHeaderSelected');
6477
        } else {
6478
            cell.removeClass('jxGridColumnHeaderSelected');
6479
        }
6480
    },
6481
    
6482
    /** 
6483
     * Method: selectColumn
6484
     * Select a column.
6485
     * This deselects a previously selected column.
6486
     *
6487
     * Parameters:
6488
     * col - {Integer} the column to select
6489
     * selected - {Boolean} the new state of the column
6490
     */
6491
    selectColumn: function(col, selected) {
6492
        /* todo: implement column selection */
6493
        if (col >= 0 && col < this.gridTable.rows[0].cells.length) {
6494
            if (selected) {
6495
                for (var i=0; i<this.gridTable.rows.length; i++) {
6496
                    this.gridTable.rows[i].cells[col + 1].addClass('jxGridColumnSelected');
6497
                }
6498
            } else {
6499
                for (var i=0; i<this.gridTable.rows.length; i++) {
6500
                    this.gridTable.rows[i].cells[col + 1].removeClass('jxGridColumnSelected');
6501
                }   
6502
            }
6503
            this.selectColumnHeader(col, selected);
6504
        }
6505
    },
6506
    
6507
    /**
6508
     * Method: onMouseMoveGrid
6509
     * handle the mouse moving over the main grid.  This pre-lights the cell,
6510
     * and subsquently the row and column (and headers).
6511
     *
6512
     * Parameters:
6513
     * e - {Event} the browser event object
6514
     */
6515
    onMouseMoveGrid: function(e) {
6516
        var rc = this.getRowColumnFromEvent(e);
6517
        if (this.options.cellPrelight) {
6518
            this.prelightCell(rc.row, rc.column);            
6519
        }
6520
        if (this.options.rowPrelight) {
6521
            this.prelightRow(rc.row);            
6522
        }
6523
        if (this.options.rowHeaderPrelight) {
6524
            this.prelightRowHeader(rc.row);            
6525
        }
6526
        if (this.options.columnPrelight) {
6527
            this.prelightColumn(rc.column);
6528
        }        
6529
        if (this.options.columnHeaderPrelight) {
6530
            this.prelightColumnHeader(rc.column);
6531
        }        
6532
    },
6533
    
6534
    /**
6535
     * Method: onMouseMoveRowHeader
6536
     * handle the mouse moving over the row header cells.  This pre-lights
6537
     * the row and subsequently the row header.
6538
     *
6539
     * Parameters:
6540
     * e - {Event} the browser event object
6541
     */
6542
    onMouseMoveRowHeader: function(e) {
6543
        if (this.options.rowPrelight) {
6544
            var rc = this.getRowColumnFromEvent(e);
6545
            this.prelightRow(rc.row);            
6546
        }
6547
    },
6548

    
6549
    /**
6550
     * Method: onMouseMoveColumnHeader
6551
     * handle the mouse moving over the column header cells.  This pre-lights
6552
     * the column and subsequently the column header.
6553
     *
6554
     * Parameters:
6555
     * e - {Event} the browser event object
6556
     */
6557
    onMouseMoveColumnHeader: function(e) {
6558
        if (this.options.columnPrelight) {
6559
            var rc = this.getRowColumnFromEvent(e);
6560
            this.prelightColumn(rc.column);
6561
        }
6562
    },
6563
    
6564
    /**
6565
     * Method: onClickGrid
6566
     * handle the user clicking on the grid.  This triggers an
6567
     * event to the model (if a cellSelected function is provided).
6568
     *
6569
     * The following is an example of a function in the model that selects
6570
     * a row when the cellSelected function is called and deselects any rows
6571
     * that are currently selected.
6572
     *
6573
     * (code)
6574
     * cellSelected: function(grid, row,col) { 
6575
     *    if (this.selectedRow != null) {
6576
     *        grid.selectRow(this.selectedRow, false);
6577
     *    }
6578
     *    this.selectedRow = row;
6579
     *    grid.selectRow(row, true);
6580
     * }
6581
     *
6582
     * Parameters:
6583
     * e - {Event} the browser event object
6584
     */
6585
    onClickGrid: function(e) {
6586
        var rc = this.getRowColumnFromEvent(e);
6587
        
6588
        if (this.options.cellSelection && this.model.cellSelected) {
6589
            this.model.cellSelected(this, rc.row, rc.column);
6590
        }
6591
        if (this.options.rowSelection && this.model.rowSelected) {
6592
            this.model.rowSelected(this, rc.row);
6593
        }
6594
        if (this.options.columnSelection && this.model.columnSelected) {
6595
            this.model.columnSelected(this, rc.column);
6596
        }
6597
        
6598
    },
6599
    
6600
    /**
6601
     * Method: onClickRowHeader
6602
     * handle the user clicking on the row header.  This triggers an
6603
     * event to the model (if a rowSelected function is provided) which
6604
     * can then select the row if desired.  
6605
     *
6606
     * The following is an example of a function in the model that selects
6607
     * a row when the rowSelected function is called and deselects any rows
6608
     * that are currently selected.  More complex code could be written to 
6609
     * allow the user to select multiple rows.
6610
     *
6611
     * (code)
6612
     * rowSelected: function(grid, row) {
6613
     *    if (this.selectedRow != null) {
6614
     *        grid.selectRow(this.selectedRow, false);
6615
     *    }
6616
     *    this.selectedRow = row;
6617
     *    grid.selectRow(row, true);
6618
     * }
6619
     * (end)
6620
     *
6621
     * Parameters:
6622
     * e - {Event} the browser event object
6623
     */
6624
    onClickRowHeader: function(e) {
6625
        var rc = this.getRowColumnFromEvent(e);
6626
        
6627
        if (this.options.rowSelection && this.model.rowSelected) {
6628
            this.model.rowSelected(this, rc.row);
6629
        }
6630
    },
6631
    
6632
    /**
6633
     * Method: onClickColumnHeader
6634
     * handle the user clicking on the column header.  This triggers column
6635
     * selection and column (and header) styling changes and an
6636
     * event to the model (if a columnSelected function is provided)
6637
     *
6638
     * The following is an example of a function in the model that selects
6639
     * a column when the columnSelected function is called and deselects any 
6640
     * columns that are currently selected.  More complex code could be written
6641
     * to allow the user to select multiple columns.
6642
     *
6643
     * (code)
6644
     * colSelected: function(grid, col) {
6645
     *    if (this.selectedColumn != null) {
6646
     *        grid.selectColumn(this.selectedColumn, false);
6647
     *    }
6648
     *    this.selectedColumn = col;
6649
     *    grid.selectColumn(col, true);
6650
     * }
6651
     * (end)
6652
     *
6653
     * Parameters:
6654
     * e - {Event} the browser event object
6655
     */
6656
    onClickColumnHeader: function(e) {
6657
        var rc = this.getRowColumnFromEvent(e);
6658
        
6659
        if (this.options.columnSelection && this.model.columnSelected) {
6660
            this.model.columnSelected(this, rc.column);
6661
        }
6662
    },
6663
    
6664
    /**
6665
     * method: getRowColumnFromEvent
6666
     * retrieve the row and column indexes from an event click.
6667
     * This function is used by the grid, row header and column
6668
     * header to safely get these numbers.
6669
     *
6670
     * If the event isn't valid (i.e. it wasn't on a TD or TH) then
6671
     * the returned values will be -1, -1
6672
     *
6673
     * Parameters:
6674
     * e - {Event} the browser event object
6675
     *
6676
     * @return Object an object with two properties, row and column,
6677
     *         that contain the row and column that was clicked
6678
     */
6679
    getRowColumnFromEvent: function(e) {
6680
        var td = e.target;
6681
        if (td.tagName != 'TD' && td.tagName != 'TH') {
6682
            return {row:-1,column:-1};
6683
        }
6684
        var tr = td.parentNode;
6685
        var col = td.cellIndex - 1; /* because of hidden spacer column */
6686
        var row = tr.rowIndex - 1; /* because of hidden spacer row */
6687
        
6688
        if (col == -1) { 
6689
            /* bug in safari returns 0 for cellIndex - only choice seems
6690
             * to be to loop through the row
6691
             */
6692
            for (var i=0; i<tr.childNodes.length; i++) {
6693
                if (tr.childNodes[i] == td) {
6694
                    col = i - 1;
6695
                    break;
6696
                }
6697
            }
6698
        }
6699
        return {row:row,column:col};
6700
    }
6701
});/**
6702
 * Class: Jx.Grid.Model
6703
 *
6704
 * Extends: Object
6705
 *
6706
 * Implements: Options, Events
6707
 *
6708
 * A Jx.Grid.Model is the source of data for a <Jx.Grid> instance.  The
6709
 * default implementation of the grid model works with two-dimensional
6710
 * arrays of data and acts as a convenient base class for custom models
6711
 * based on other sources of data.
6712
 *
6713
 * License: 
6714
 * Copyright (c) 2008, DM Solutions Group Inc.
6715
 * 
6716
 * This file is licensed under an MIT style license
6717
 */
6718
Jx.Grid.Model = new Class({
6719
    Family: 'Jx.Grid.Model',
6720
    Implements: [Events, Options],
6721
    options: {
6722
        /* Option: colHeaderHeight
6723
         * default 28, the height of the column header row
6724
         */
6725
        colHeaderHeight: 28,
6726
        /* Option: colHeaderHeight
6727
         * default 28, the height of the column header row
6728
         */
6729
        rowHeaderWidth: 28,
6730
        /* Option: rowHeaderWidth
6731
         * default 28, the width of the row header column.
6732
         */
6733
        colWidth: 50,
6734
        /* Option: colWidth
6735
         * default 50, the width of columns
6736
         */
6737
        rowHeight: 20,
6738
        /* Option: rowHeight
6739
         * default 20, the height of rows
6740
         */
6741
        rowHeaders: null,
6742
        /* Option: columnHeaders
6743
         * optional column headers, defaults to null
6744
         */
6745
        columnHeaders: null
6746
    },
6747
    data: null,
6748
    /**
6749
     * Constructor: Jx.Grid.Model
6750
     * create a new grid model
6751
     *
6752
     * Parameters:
6753
     * data - array of data to display in the grid
6754
     * options - <Jx.Grid.Model.Options>
6755
     */
6756
    initialize: function(data, options) {
6757
        this.data = data || [];
6758
        this.setOptions(options);
6759
    },
6760
    /** 
6761
     * Method: getColumnCount
6762
     * This function returns the number of columns of data in the 
6763
     * model as an integer value.
6764
     */ 
6765
    getColumnCount: function() { return (this.data && this.data[0]) ? this.data[0].length : 0; },
6766
    /* Method: getColumnHeaderHTML
6767
     * This function returns an HTML string to be placed in the
6768
     * column header for the given column index.
6769
     */ 
6770
    getColumnHeaderHTML: function(col) { 
6771
        return this.options.columnHeaders?this.options.columnHeaders[col]:col+1;
6772
     },
6773
     /* Method: getColumnHeaderHeight
6774
      * This function returns an integer which is the height of the
6775
      * column header row in pixels.
6776
      */ 
6777
    getColumnHeaderHeight: function() { return this.options.colHeaderHeight; },
6778
    /* Method: getColumnWidth
6779
     * This function returns an integer which is the width of the
6780
     * given column in pixels.
6781
     */ 
6782
    getColumnWidth: function(col) { return this.options.colWidth; },
6783
    /* Method: getRowHeaderHTML
6784
     * This function returns an HTML string to be placed in the row
6785
     * header for the given row index
6786
     */ 
6787
    getRowHeaderHTML: function(row) { 
6788
        return this.options.rowHeaders?this.options.rowHeaders[row]:row+1; 
6789
    },
6790
    /* Method: getRowHeaderWidth
6791
     * This function returns an integer which is the width of the row
6792
     * header column in pixels.
6793
     */ 
6794
    getRowHeaderWidth: function() { return this.options.rowHeaderWidth; },
6795
    /* Method: getRowHeight
6796
     * This function returns an integer which is the height of the
6797
     * given row in pixels.
6798
     */ 
6799
    getRowHeight: function(row) { return this.options.rowHeight; },
6800
    /* Method: getRowCount
6801
     * This function returns the number of rows of data in the model
6802
     * as an integer value.
6803
     */ 
6804
    getRowCount: function() { return this.data.length },
6805
    /* Method: getValueAt
6806
     * This function returns an HTML string which is the text to place
6807
     * in the cell at the given row and column.
6808
     */ 
6809
    getValueAt: function(row, col) { return (this.data && $chk(this.data[row])) ? this.data[row][col] : ''; },
6810
    /* Method: setColumnWidth
6811
     * This function is called with a column index and width in pixels
6812
     * when a column is resized.  This function is only required if the grid
6813
     * allows resizeable columns.
6814
     */
6815
    setColumnWidth: function() {},
6816
    /* Method: isCellEditable
6817
     * This function returns a boolean value to indicate if a given
6818
     * cell is editable by the user.
6819
     */ 
6820
    isCellEditable: function() { return false },
6821
    /* Method: setValueAt
6822
     * This function is called with the row and column of a cell and a
6823
     * new value for the cell.  It is mandatory to provide this function if any of
6824
     * the cells in the model are editable.
6825
     */ 
6826
    setValueAt: function(row, col, value) {},
6827
    /* Method: rowSelected
6828
     * This function is called by the grid to indicate that the user
6829
     * has selected a row by clicking on the row header.
6830
     */ 
6831
    rowSelected: function(grid, row) {
6832
        if (this.selectedRow != null) {
6833
            grid.selectRow(this.selectedRow, false);
6834
        }
6835
        this.selectedRow = row;
6836
        grid.selectRow(row, true);
6837
        this.fireEvent('select-row', row);
6838
    },
6839
    /* Method: columnSelected
6840
     * This function is called by the grid to indicate that the user
6841
     * has selected a column by clicking on the column header.
6842
     */ 
6843
    columnSelected: function(grid, col) {
6844
        if (this.selectedCol != null) {
6845
            grid.selectColumn(this.selectedCol, false);
6846
        }
6847
        this.selectedCol = col;
6848
        grid.selectColumn(col, true);
6849
        this.fireEvent('select-column', col);
6850
    },
6851
    /* Method: cellSelected
6852
     * This function is called by the grid to indicate that the user
6853
     * has selected a cell by clicking on the cell in the grid.
6854
     */
6855
    cellSelected: function(grid, row,col) { 
6856
        grid.selectCell(row, col);
6857
        this.fireEvent('select-cell', [row, col]);
6858
    
6859
    }
6860
});
6861
// $Id: context.js 423 2009-05-12 12:37:56Z pagameba $
6862
/**
6863
 * Class: Jx.Menu.Context
6864
 *
6865
 * Extends: Jx.Menu
6866
 *
6867
 * A <Jx.Menu> that has no button but can be opened at a specific 
6868
 * browser location to implement context menus (for instance).
6869
 *
6870
 * Example:
6871
 * (code)
6872
 * (end)
6873
 *
6874
 * Events:
6875
 * TODO - add open/close events?
6876
 *
6877
 * License: 
6878
 * Copyright (c) 2008, DM Solutions Group Inc.
6879
 * 
6880
 * This file is licensed under an MIT style license
6881
 */
6882
Jx.Menu.Context = new Class({
6883
    Family: 'Jx.Menu.Context',
6884
    /** Extends:
6885
     * <Jx.Menu>
6886
     */
6887
    Extends: Jx.Menu,
6888
    /**
6889
     * Constructor: Jx.ContextMenu
6890
     * create a new context menu
6891
     *
6892
     * Parameters:
6893
     * id - {HTMLElement} element or id to make this the context menu
6894
     * for.  The menu hooks the oncontextmenu event of the element
6895
     * and shows itself at the mouse position where the right-click
6896
     * happened.
6897
     */
6898
    initialize : function(id) {
6899
        this.parent();
6900
        if ($(id)) {
6901
            $(id).addEvent('contextmenu', this.show.bindWithEvent(this));
6902
        }
6903
    },
6904
    /**
6905
     * Method: show
6906
     * Show the context menu at the location of the mouse click
6907
     *
6908
     * Parameters:
6909
     * e - {Event} the mouse event
6910
     */
6911
    show : function(e) {
6912
        if (this.items.length ==0) {
6913
            return;
6914
        }
6915
        
6916
        this.contentContainer.setStyle('visibility','hidden');
6917
        this.contentContainer.setStyle('display','block');
6918
        $(document.body).adopt(this.contentContainer);            
6919
        /* we have to size the container for IE to render the chrome correctly
6920
         * but just in the menu/sub menu case - there is some horrible peekaboo
6921
         * bug in IE related to ULs that we just couldn't figure out
6922
         */
6923
        this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
6924
        
6925
        this.position(this.contentContainer, document.body, {
6926
            horizontal: [e.page.x + ' left'],
6927
            vertical: [e.page.y + ' top', e.page.y + ' bottom'],
6928
            offsets: this.chromeOffsets
6929
        });
6930

    
6931
        this.contentContainer.setStyle('visibility','');
6932
        this.showChrome(this.contentContainer);
6933
                
6934
        document.addEvent('mousedown', this.hideWatcher);
6935
        document.addEvent('keyup', this.keypressWatcher);
6936

    
6937
        e.stop();
6938
    }    
6939
});// $Id: menu.separator.js 423 2009-05-12 12:37:56Z pagameba $
6940
/**
6941
 * Class: Jx.Menu.Separator
6942
 *
6943
 * Extends: Object
6944
 *
6945
 * A convenience class to create a visual separator in a menu.
6946
 *
6947
 * Example:
6948
 * (code)
6949
 * (end)
6950
 *
6951
 * License: 
6952
 * Copyright (c) 2008, DM Solutions Group Inc.
6953
 * 
6954
 * This file is licensed under an MIT style license
6955
 */
6956
Jx.Menu.Separator = new Class({
6957
    Family: 'Jx.Menu.Separator',
6958
    /**
6959
     * Property: domObj
6960
     * {HTMLElement} the HTML element that the separator is contained
6961
     * within
6962
     */
6963
    domObj: null,
6964
    /**
6965
     * Property: owner
6966
     * {<Jx.Menu>, <Jx.Menu.SubMenu>} the menu that the separator is in.
6967
     */
6968
    owner: null,
6969
    /**
6970
     * Constructor: Jx.Menu.Separator
6971
     * Create a new instance of a menu separator
6972
     */
6973
    initialize: function() {
6974
        this.domObj = new Element('li',{'class':'jxMenuItem'});
6975
        var span = new Element('span', {'class':'jxMenuSeparator','html':'&nbsp;'});
6976
        this.domObj.appendChild(span);
6977
    },
6978
    /**
6979
     * Method: setOwner
6980
     * Set the ownder of this menu item
6981
     *
6982
     * Parameters:
6983
     * obj - {Object} the new owner
6984
     */
6985
    setOwner: function(obj) {
6986
        this.owner = obj;
6987
    },
6988
    /**
6989
     * Method: hide
6990
     * Hide the menu item.
6991
     */
6992
    hide: $empty,
6993
    /**
6994
     * Method: show
6995
     * Show the menu item
6996
     */
6997
    show: $empty
6998
});// $Id: submenu.js 423 2009-05-12 12:37:56Z pagameba $
6999
/**
7000
 * Class: Jx.Menu.SubMenu
7001
 *
7002
 * Extends: <Jx.Menu.Item>
7003
 *
7004
 * Implements: <Jx.AutoPosition>, <Jx.Chrome>
7005
 *
7006
 * A sub menu contains menu items within a main menu or another
7007
 * sub menu.
7008
 *
7009
 * The structure of a SubMenu is the same as a <Jx.Menu.Item> with
7010
 * an additional unordered list element appended to the container.
7011
 *
7012
 * Example:
7013
 * (code)
7014
 * (end)
7015
 *
7016
 * License: 
7017
 * Copyright (c) 2008, DM Solutions Group Inc.
7018
 * 
7019
 * This file is licensed under an MIT style license
7020
 */
7021
Jx.Menu.SubMenu = new Class({
7022
    Family: 'Jx.Menu.SubMenu',
7023
    Extends: Jx.Menu.Item,
7024
    Implements: [Jx.AutoPosition, Jx.Chrome],
7025
    /**
7026
     * Property: subDomObj
7027
     * {HTMLElement} the HTML container for the sub menu.
7028
     */
7029
    subDomObj: null,
7030
    /**
7031
     * Property: owner
7032
     * {<Jx.Menu> or <Jx.SubMenu>} the menu or sub menu that this sub menu
7033
     * belongs
7034
     */
7035
    owner: null,
7036
    /**
7037
     * Property: visibleItem
7038
     * {<Jx.MenuItem>} the visible item within the menu
7039
     */
7040
    visibleItem: null,
7041
    /**
7042
     * Property: items
7043
     * {Array} the menu items that are in this sub menu.
7044
     */
7045
    items: null,
7046
    /**
7047
     * Constructor: Jx.SubMenu
7048
     * Create a new instance of Jx.SubMenu
7049
     *
7050
     * Parameters:
7051
     * options - see <Jx.Button.Options>
7052
     */
7053
    initialize: function(options) { 
7054
        this.open = false;
7055
        this.items = [];
7056
        this.parent(options);
7057
        this.domA.addClass('jxButtonSubMenu');
7058
        
7059
        this.contentContainer = new Element('div', {
7060
            'class': 'jxMenuContainer'
7061
        });
7062
        this.subDomObj = new Element('ul', {
7063
            'class':'jxSubMenu'
7064
        });
7065
        this.contentContainer.adopt(this.subDomObj);
7066
    },
7067
    /**
7068
     * Method: setOwner
7069
     * Set the owner of this sub menu
7070
     *
7071
     * Parameters:
7072
     * obj - {Object} the owner
7073
     */
7074
    setOwner: function(obj) {
7075
        this.owner = obj;
7076
    },
7077
    /**
7078
     * Method: show
7079
     * Show the sub menu
7080
     */
7081
    show: function() {
7082
        if (this.open || this.items.length == 0) {
7083
            return;
7084
        }
7085
        
7086
        this.contentContainer.setStyle('visibility','hidden');
7087
        this.contentContainer.setStyle('display','block');
7088
        $(document.body).adopt(this.contentContainer);            
7089
        /* we have to size the container for IE to render the chrome correctly
7090
         * but just in the menu/sub menu case - there is some horrible peekaboo
7091
         * bug in IE related to ULs that we just couldn't figure out
7092
         */
7093
        this.contentContainer.setContentBoxSize(this.subDomObj.getMarginBoxSize());
7094
        this.showChrome(this.contentContainer);
7095
        
7096
        this.position(this.contentContainer, this.domObj, {
7097
            horizontal: ['right left', 'left right'],
7098
            vertical: ['top top'],
7099
            offsets: this.chromeOffsets
7100
        });
7101
        
7102
        this.open = true;
7103
        this.contentContainer.setStyle('visibility','');
7104
        
7105
        this.setActive(true);
7106
    },
7107
    
7108
    eventInMenu: function(e) {
7109
        if (this.visibleItem && 
7110
            this.visibleItem.eventInMenu && 
7111
            this.visibleItem.eventInMenu(e)) {
7112
            return true;
7113
        }
7114
        return $(e.target).descendantOf(this.domObj) ||
7115
               $(e.target).descendantOf(this.subDomObj) ||
7116
               this.items.some(
7117
                   function(item) {
7118
                       return item instanceof Jx.Menu.SubMenu && 
7119
                              item.eventInMenu(e);
7120
                   }
7121
               );
7122
    },
7123
    
7124
    /**
7125
     * Method: hide
7126
     * Hide the sub menu
7127
     */
7128
    hide: function() {
7129
        if (!this.open) {
7130
            return;
7131
        }
7132
        this.open = false;
7133
        this.items.each(function(item){item.hide();});
7134
        this.contentContainer.setStyle('display','none');
7135
        this.visibleItem = null;
7136
    },
7137
    /**
7138
     * Method: add
7139
     * Add menu items to the sub menu.
7140
     *
7141
     * Parameters:
7142
     * item - {<Jx.MenuItem>} the menu item to add.  Multiple menu items
7143
     * can be added by passing multiple arguments to this function.
7144
     */
7145
    add : function() { /* menu */
7146
        var that = this;
7147
        $A(arguments).each(function(item){
7148
            that.items.push(item);
7149
            item.setOwner(that);
7150
            that.subDomObj.adopt(item.domObj);
7151
        });
7152
        return this;
7153
    },
7154
    /**
7155
     * Method: insertBefore
7156
     * Insert a menu item before another menu item.
7157
     *
7158
     * Parameters:
7159
     * newItem - {<Jx.MenuItem>} the menu item to insert
7160
     * targetItem - {<Jx.MenuItem>} the menu item to insert before
7161
     */
7162
    insertBefore: function(newItem, targetItem) {
7163
        var bInserted = false;
7164
        for (var i=0; i<this.items.length; i++) {
7165
            if (this.items[i] == targetItem) {
7166
                this.items.splice(i, 0, newItem);
7167
                this.subDomObj.insertBefore(newItem.domObj, targetItem.domObj);
7168
                bInserted = true;
7169
                break;
7170
            }
7171
        }
7172
        if (!bInserted) {
7173
            this.add(newItem);
7174
        }
7175
    },
7176
    /**
7177
     * Method: remove
7178
     * Remove a single menu item from the menu.
7179
     *
7180
     * Parameters:
7181
     * item - {<Jx.MenuItem} the menu item to remove.
7182
     */
7183
    remove: function(item) {
7184
        for (var i=0; i<this.items.length; i++) {
7185
            if (this.items[i] == item) {
7186
                this.items.splice(i,1);
7187
                this.subDomObj.removeChild(item.domObj);
7188
                break;
7189
            }
7190
        }
7191
    },
7192
    /**
7193
     * Method: deactivate
7194
     * Deactivate the sub menu
7195
     *
7196
     * Parameters:
7197
     * e - {Event} the event that triggered the menu being
7198
     * deactivated.
7199
     */
7200
    deactivate: function(e) {
7201
        if (this.owner) {
7202
            this.owner.deactivate(e);            
7203
        }
7204
    },
7205
    /**
7206
     * Method: isActive
7207
     * Indicate if this sub menu is active
7208
     *
7209
     * Returns:
7210
     * {Boolean} true if the <Jx.Menu> that ultimately contains
7211
     * this sub menu is active, false otherwise.
7212
     */
7213
    isActive: function() { 
7214
        if (this.owner) {
7215
            return this.owner.isActive();
7216
        } else {
7217
            return false;
7218
        }
7219
    },
7220
    /**
7221
     * Method: setActive
7222
     * Set the active state of the <Jx.Menu> that contains this sub menu
7223
     *
7224
     * Parameters:
7225
     * isActive - {Boolean} the new active state
7226
     */
7227
    setActive: function(isActive) { 
7228
        if (this.owner && this.owner.setActive) {
7229
            this.owner.setActive(isActive);
7230
        }
7231
    },
7232
    /**
7233
     * Method: setVisibleItem
7234
     * Set a sub menu of this menu to be visible and hide the previously
7235
     * visible one.
7236
     *
7237
     * Parameters: 
7238
     * obj - {<Jx.SubMenu>} the sub menu that should be visible
7239
     */
7240
    setVisibleItem: function(obj) {
7241
        if (this.visibleItem != obj) {
7242
            if (this.visibleItem && this.visibleItem.hide) {
7243
                this.visibleItem.hide();
7244
            }
7245
            this.visibleItem = obj;
7246
            this.visibleItem.show();
7247
        }
7248
    }
7249
});// $Id: snap.js 423 2009-05-12 12:37:56Z pagameba $
7250
/**
7251
 * Class: Jx.Splitter.Snap
7252
 *
7253
 * Extends: Object
7254
 *
7255
 * A helper class to create an element that can snap a split panel open or
7256
 * closed.
7257
 *
7258
 * Example:
7259
 * (code)
7260
 * (end)
7261
 *
7262
 * License: 
7263
 * Copyright (c) 2008, DM Solutions Group Inc.
7264
 * 
7265
 * This file is licensed under an MIT style license
7266
 */
7267
Jx.Splitter.Snap = new Class({
7268
    Family: 'Jx.Splitter.Snap',
7269
    /**
7270
     * Property: snap
7271
     * {HTMLElement} the DOM element of the snap (the thing that gets
7272
     * clicked).
7273
     */
7274
    snap: null,
7275
    /**
7276
     * Property: element
7277
     * {HTMLElement} An element of the <Jx.Splitter> that gets controlled
7278
     * by this snap
7279
     */
7280
    element: null,
7281
    /**
7282
     * Property: splitter
7283
     * {<Jx.Splitter>} the splitter that this snap is associated with.
7284
     */
7285
    splitter: null,
7286
    /**
7287
     * Property: layout
7288
     * {String} track the layout of the splitter for convenience.
7289
     */
7290
    layout: 'vertical',
7291
    /**
7292
     * Constructor: Jx.Splitter.Snap
7293
     * Create a new Jx.Splitter.Snap
7294
     *
7295
     * Parameters:
7296
     * snap - {HTMLElement} the clickable thing that snaps the element
7297
     *           open and closed
7298
     * element - {HTMLElement} the element that gets controlled by the snap
7299
     * splitter - {<Jx.Splitter>} the splitter that this all happens inside of.
7300
     */
7301
    initialize: function(snap, element, splitter, events) {
7302
        this.snap = snap;
7303
        this.element = element;
7304
        var jxl = element.retrieve('jxLayout');
7305
        jxl.addEvent('sizeChange', this.sizeChange.bind(this));
7306
        this.splitter = splitter;
7307
        this.layout = splitter.options.layout; 
7308
        var jxo = jxl.options;
7309
        var size = this.element.getContentBoxSize();
7310
        if (this.layout == 'vertical') {
7311
            this.originalSize = size.height;
7312
            this.minimumSize = jxo.minHeight ? jxo.minHeight : 0;
7313
        } else {
7314
            this.originalSize = size.width;
7315
            this.minimumSize = jxo.minWidth ? jxo.minWidth : 0;
7316
        }
7317
        events.each(function(eventName) {
7318
            snap.addEvent(eventName, this.toggleElement.bind(this));
7319
        }, this);
7320
    },
7321
    
7322
    /**
7323
     * Method: toggleElement
7324
     * Snap the element open or closed.
7325
     */
7326
    toggleElement: function() {
7327
        var size = this.element.getContentBoxSize();
7328
        var newSize = {};
7329
        if (this.layout == 'vertical') {
7330
            if (size.height == this.minimumSize) {
7331
                newSize.height = this.originalSize;
7332
            } else {
7333
                this.originalSize = size.height;
7334
                newSize.height = this.minimumSize;
7335
            }
7336
        } else {
7337
            if (size.width == this.minimumSize) {
7338
                newSize.width = this.originalSize;
7339
            } else {
7340
                this.originalSize = size.width;
7341
                newSize.width = this.minimumSize;
7342
            }
7343
        }
7344
        this.element.resize(newSize);
7345
        this.splitter.sizeChanged();
7346
    },
7347
    
7348
    /**
7349
     * Method: sizeChanged
7350
     * Handle the size of the element changing to see if the
7351
     * toggle state has changed.
7352
     */
7353
    sizeChange: function() {
7354
        var size = this.element.getContentBoxSize();
7355
        if (this.layout == 'vertical') {
7356
            if (size.height == this.minimumSize) {
7357
                this.snap.addClass('jxSnapClosed');
7358
                this.snap.removeClass('jxSnapOpened');
7359
            } else {
7360
                this.snap.addClass('jxSnapOpened');
7361
                this.snap.removeClass('jxSnapClosed');
7362
            }
7363
        } else {
7364
            if (size.width == this.minimumSize) {
7365
                this.snap.addClass('jxSnapClosed');
7366
                this.snap.removeClass('jxSnapOpened');
7367
            } else {
7368
                this.snap.addClass('jxSnapOpened');
7369
                this.snap.removeClass('jxSnapClosed');
7370
            }
7371
        }
7372
    }
7373
});// $Id: toolbar.js 433 2009-05-14 12:49:46Z pagameba $
7374
/**
7375
 * Class: Jx.Toolbar
7376
 *
7377
 * Extends: Object
7378
 *
7379
 * Implements: Options, Events
7380
 *
7381
 * A toolbar is a container object that contains other objects such as
7382
 * buttons.  The toolbar organizes the objects it contains automatically,
7383
 * wrapping them as necessary.  Multiple toolbars may be placed within
7384
 * the same containing object.
7385
 *
7386
 * Jx.Toolbar includes CSS classes for styling the appearance of a
7387
 * toolbar to be similar to traditional desktop application toolbars.
7388
 *
7389
 * There is one special object, Jx.ToolbarSeparator, that provides
7390
 * a visual separation between objects in a toolbar.
7391
 *
7392
 * While a toolbar is generally a *dumb* container, it serves a special
7393
 * purpose for menus by providing some infrastructure so that menus can behave
7394
 * properly.
7395
 *
7396
 * In general, almost anything can be placed in a Toolbar, and mixed with 
7397
 * anything else.
7398
 *
7399
 * Example:
7400
 * The following example shows how to create a Jx.Toolbar instance and place
7401
 * two objects in it.
7402
 *
7403
 * (code)
7404
 * //myToolbarContainer is the id of a <div> in the HTML page.
7405
 * function myFunction() {}
7406
 * var myToolbar = new Jx.Toolbar('myToolbarContainer');
7407
 * 
7408
 * var myButton = new Jx.Button(buttonOptions);
7409
 *
7410
 * var myElement = document.createElement('select');
7411
 *
7412
 * myToolbar.add(myButton, new Jx.ToolbarSeparator(), myElement);
7413
 * (end)
7414
 *
7415
 * Events:
7416
 * add - fired when one or more buttons are added to a toolbar
7417
 * remove - fired when on eor more buttons are removed from a toolbar
7418
 *
7419
 * Implements: 
7420
 * Options
7421
 *
7422
 * License: 
7423
 * Copyright (c) 2008, DM Solutions Group Inc.
7424
 * 
7425
 * This file is licensed under an MIT style license
7426
 */
7427
Jx.Toolbar = new Class({
7428
    Family: 'Jx.Toolbar',
7429
    Implements: [Options,Events],
7430
    /**
7431
     * Property: items
7432
     * {Array} an array of the things in the toolbar.
7433
     */
7434
    items : null,
7435
    /**
7436
     * Property: domObj
7437
     * {HTMLElement} the HTML element that the toolbar lives in
7438
     */
7439
    domObj : null,
7440
    /**
7441
     * Property: isActive
7442
     * When a toolbar contains <Jx.Menu> instances, they want to know
7443
     * if any menu in the toolbar is active and this is how they
7444
     * find out.
7445
     */
7446
    isActive : false,
7447
    options: {
7448
        type: 'Toolbar',
7449
        /* Option: position
7450
         * the position of this toolbar in the container.  The position
7451
         * affects some items in the toolbar, such as menus and flyouts, which
7452
         * need to open in a manner sensitive to the position.  May be one of
7453
         * 'top', 'right', 'bottom' or 'left'.  Default is 'top'.
7454
        position: 'top',
7455
        */
7456
        /* Option: parent
7457
         * a DOM element to add this toolbar to
7458
         */
7459
        parent: null,
7460
        /* Option: autoSize
7461
         * if true, the toolbar will attempt to set its size based on the
7462
         * things it contains.  Default is false.
7463
         */
7464
        autoSize: false,
7465
        /* Option: scroll
7466
         * if true, the toolbar may scroll if the contents are wider than
7467
         * the size of the toolbar
7468
         */
7469
        scroll: true
7470
    },
7471
    /**
7472
     * Constructor: Jx.Toolbar
7473
     * Create a new instance of Jx.Toolbar.
7474
     *
7475
     * Parameters:
7476
     * options - <Jx.Toolbar.Options>
7477
     */
7478
    initialize : function(options) {
7479
        this.setOptions(options);
7480
        this.items = [];
7481
        
7482
        this.domObj = new Element('ul', {
7483
            id: this.options.id,
7484
            'class':'jx'+this.options.type
7485
        });
7486
        
7487
        if (this.options.parent) {
7488
            this.addTo(this.options.parent);
7489
        }
7490
        this.deactivateWatcher = this.deactivate.bindWithEvent(this);
7491
        if (this.options.items) {
7492
            this.add(this.options.items);
7493
        }
7494
    },
7495
    
7496
    /**
7497
     * Method: addTo
7498
     * add this toolbar to a DOM element automatically creating a toolbar
7499
     * container if necessary
7500
     *
7501
     * Parameters:
7502
     * parent - the DOM element or toolbar container to add this toolbar to.
7503
     */
7504
    addTo: function(parent) {
7505
        var tbc = $(parent).retrieve('jxBarContainer');
7506
        if (!tbc) {
7507
            tbc = new Jx.Toolbar.Container({
7508
                parent: parent, 
7509
                position: this.options.position, 
7510
                autoSize: this.options.autoSize,
7511
                scroll: this.options.scroll
7512
            });
7513
        }
7514
        tbc.add(this);
7515
        return this;
7516
    },
7517
    
7518
    /**
7519
     * Method: add
7520
     * Add an item to the toolbar.  If the item being added is a Jx component
7521
     * with a domObj property, the domObj is added.  If the item being added
7522
     * is an LI element, then it is given a CSS class of *jxToolItem*.
7523
     * Otherwise, the thing is wrapped in a <Jx.ToolbarItem>.
7524
     *
7525
     * Parameters:
7526
     * thing - {Object} the thing to add.  More than one thing can be added
7527
     * by passing multiple arguments.
7528
     */
7529
    add: function( ) {
7530
        $A(arguments).flatten().each(function(thing) {
7531
            if (thing.domObj) {
7532
                thing = thing.domObj;
7533
            }
7534
            if (thing.tagName == 'LI') {
7535
                if (!thing.hasClass('jxToolItem')) {
7536
                    thing.addClass('jxToolItem');
7537
                }
7538
                this.domObj.appendChild(thing);
7539
            } else {
7540
                var item = new Jx.Toolbar.Item(thing);
7541
                this.domObj.appendChild(item.domObj);
7542
            }            
7543
        }, this);
7544

    
7545
        if (arguments.length > 0) {
7546
            this.fireEvent('add', this);
7547
        }
7548
        return this;
7549
    },
7550
    /**
7551
     * Method: remove
7552
     * remove an item from a toolbar.  If the item is not in this toolbar
7553
     * nothing happens
7554
     *
7555
     * Parameters:
7556
     * item - {Object} the object to remove
7557
     *
7558
     * Returns:
7559
     * {Object} the item that was removed, or null if the item was not
7560
     * removed.
7561
     */
7562
    remove: function(item) {
7563
        if (item.domObj) {
7564
            item = item.domObj;
7565
        }
7566
        var li = item.findElement('LI');
7567
        if (li && li.parentNode == this.domObj) {
7568
            item.dispose();
7569
            li.dispose();
7570
            this.fireEvent('remove', this);
7571
        } else {
7572
            return null;
7573
        }
7574
    },
7575
    /**
7576
     * Method: deactivate
7577
     * Deactivate the Toolbar (when it is acting as a menu bar).
7578
     */
7579
    deactivate: function() {
7580
        this.items.each(function(o){o.hide();});
7581
        this.setActive(false);
7582
    },
7583
    /**
7584
     * Method: isActive
7585
     * Indicate if the toolbar is currently active (as a menu bar)
7586
     *
7587
     * Returns:
7588
     * {Boolean}
7589
     */
7590
    isActive: function() { 
7591
        return this.isActive; 
7592
    },
7593
    /**
7594
     * Method: setActive
7595
     * Set the active state of the toolbar (for menus)
7596
     *
7597
     * Parameters: 
7598
     * b - {Boolean} the new state
7599
     */
7600
    setActive: function(b) { 
7601
        this.isActive = b;
7602
        if (this.isActive) {
7603
            document.addEvent('click', this.deactivateWatcher);
7604
        } else {
7605
            document.removeEvent('click', this.deactivateWatcher);
7606
        }
7607
    },
7608
    /**
7609
     * Method: setVisibleItem
7610
     * For menus, they want to know which menu is currently open.
7611
     *
7612
     * Parameters:
7613
     * obj - {<Jx.Menu>} the menu that just opened.
7614
     */
7615
    setVisibleItem: function(obj) {
7616
        if (this.visibleItem && this.visibleItem.hide && this.visibleItem != obj) {
7617
            this.visibleItem.hide();
7618
        }
7619
        this.visibleItem = obj;
7620
        if (this.isActive()) {
7621
            this.visibleItem.show();
7622
        }
7623
    },
7624
    showItem: function(item) {
7625
        this.fireEvent('show', item);
7626
    }
7627
});
7628
// $Id: tabset.js 423 2009-05-12 12:37:56Z pagameba $
7629
/**
7630
 * Class: Jx.TabSet
7631
 *
7632
 * Extends: Object
7633
 *
7634
 * Implements: Options, Events
7635
 *
7636
 * A TabSet manages a set of <Jx.Button.Tab> content areas by ensuring that only one
7637
 * of the content areas is visible (i.e. the active tab).  TabSet does not
7638
 * manage the actual tabs.  The instances of <Jx.Button.Tab> that are to be managed
7639
 * as a set have to be added to both a TabSet and a <Jx.Toolbar>.  The content
7640
 * areas of the <Jx.Button.Tab>s are sized to fit the content area that the TabSet
7641
 * is managing.
7642
 *
7643
 * Example:
7644
 * (code)
7645
 * var tabBar = new Jx.Toolbar('tabBar');
7646
 * var tabSet = new Jx.TabSet('tabArea');
7647
 * 
7648
 * var tab1 = new Jx.Button.Tab('tab 1', {contentID: 'content1'});
7649
 * var tab2 = new Jx.Button.Tab('tab 2', {contentID: 'content2'});
7650
 * var tab3 = new Jx.Button.Tab('tab 3', {contentID: 'content3'});
7651
 * var tab4 = new Jx.Button.Tab('tab 4', {contentURL: 'test_content.html'});
7652
 * 
7653
 * tabSet.add(t1, t2, t3, t4);
7654
 * tabBar.add(t1, t2, t3, t4);
7655
 * (end)
7656
 *
7657
 * Events:
7658
 * tabChange - the current tab has changed
7659
 *
7660
 * License: 
7661
 * Copyright (c) 2008, DM Solutions Group Inc.
7662
 * 
7663
 * This file is licensed under an MIT style license
7664
 */
7665
Jx.TabSet = new Class({
7666
    Family: 'Jx.TabSet',
7667
    Implements: [Options,Events],
7668
    /**
7669
     * Property: tabs
7670
     * {Array} array of tabs that are managed by this tab set
7671
     */
7672
    tabs: null,
7673
    /**
7674
     * Property: domObj
7675
     * {HTMLElement} The HTML element that represents this tab set in the DOM.
7676
     * The content areas of each tab are sized to fill the domObj.
7677
     */
7678
    domObj : null,
7679
    /**
7680
     * Constructor: Jx.TabSet
7681
     * Create a new instance of <Jx.TabSet> within a specific element of
7682
     * the DOM.
7683
     *
7684
     * Parameters:
7685
     * domObj - {HTMLElement} an element or id of an element to put the
7686
     * content of the tabs into.
7687
     * options - an options object, only event handlers are supported
7688
     * as options at this time.
7689
     */
7690
    initialize: function(domObj, options) {
7691
        this.setOptions(options);
7692
        this.tabs = [];
7693
        this.domObj = $(domObj);
7694
        if (!this.domObj.hasClass('jxTabSetContainer')) {
7695
            this.domObj.addClass('jxTabSetContainer');
7696
        }
7697
        this.setActiveTabFn = this.setActiveTab.bind(this);
7698
    },
7699
    /**
7700
     * Method: resizeTabBox
7701
     * Resize the tab set content area and propogate the changes to
7702
     * each of the tabs managed by the tab set.
7703
     */
7704
    resizeTabBox: function() {
7705
        if (this.activeTab && this.activeTab.content.resize) {
7706
            this.activeTab.content.resize({forceResize: true});
7707
        }
7708
    },
7709
    
7710
    /**
7711
     * Method: add
7712
     * Add one or more <Jx.Button.Tab>s to the TabSet.
7713
     *
7714
     * Parameters:
7715
     * tab - {<Jx.Tab>} an instance of <Jx.Tab> to add to the tab set.  More
7716
     * than one tab can be added by passing extra parameters to this method.
7717
     */
7718
    add: function() {
7719
        $A(arguments).each(function(tab) {
7720
            if (tab instanceof Jx.Button.Tab) {
7721
                tab.addEvent('down',this.setActiveTabFn);
7722
                tab.tabSet = this;
7723
                this.domObj.appendChild(tab.content);
7724
                this.tabs.push(tab);
7725
                if ((!this.activeTab || tab.options.active) && tab.options.enabled) {
7726
                    tab.options.active = false;
7727
                    tab.setActive(true);
7728
                }
7729
            }
7730
        }, this);
7731
        return this;
7732
    },
7733
    /**
7734
     * Method: remove
7735
     * Remove a tab from this TabSet.  Note that it is the caller's responsibility
7736
     * to remove the tab from the <Jx.Toolbar>.
7737
     *
7738
     * Parameters:
7739
     * tab - {<Jx.Tab>} the tab to remove.
7740
     */
7741
    remove: function(tab) {
7742
        if (tab instanceof Jx.Button.Tab && this.tabs.indexOf(tab) != -1) {
7743
            this.tabs.erase(tab);
7744
            if (this.activeTab == tab) {
7745
                if (this.tabs.length) {
7746
                    this.tabs[0].setActive(true);
7747
                }
7748
            }
7749
            tab.removeEvent('down',this.setActiveTabFn);
7750
            tab.content.dispose();
7751
        }
7752
    },
7753
    /**
7754
     * Method: setActiveTab
7755
     * Set the active tab to the one passed to this method
7756
     *
7757
     * Parameters:
7758
     * tab - {<Jx.Button.Tab>} the tab to make active.
7759
     */
7760
    setActiveTab: function(tab) {
7761
        if (this.activeTab && this.activeTab != tab) {
7762
            this.activeTab.setActive(false);
7763
        }
7764
        this.activeTab = tab;
7765
        if (this.activeTab.content.resize) {
7766
          this.activeTab.content.resize({forceResize: true});
7767
        }
7768
        this.fireEvent('tabChange', [this, tab]);
7769
    }
7770
});
7771

    
7772

    
7773

    
7774
// $Id: tabbox.js 425 2009-05-12 15:27:23Z pagameba $
7775
/**
7776
 * Class: Jx.TabBox
7777
 * 
7778
 * Extends: Object
7779
 *
7780
 * Implements: Options, Events, <Jx.Addable>
7781
 *
7782
 * A convenience class to handle the common case of a single toolbar
7783
 * directly attached to the content area of the tabs.  It manages both a
7784
 * <Jx.Toolbar> and a <Jx.TabSet> so that you don't have to.  If you are using
7785
 * a TabBox, then tabs only have to be added to the TabBox rather than to
7786
 * both a <Jx.TabSet> and a <Jx.Toolbar>.
7787
 *
7788
 * Example:
7789
 * (code)
7790
 * var tabBox = new Jx.TabBox('subTabArea', 'top');
7791
 * 
7792
 * var tab1 = new Jx.Button.Tab('Tab 1', {contentID: 'content4'});
7793
 * var tab2 = new Jx.Button.Tab('Tab 2', {contentID: 'content5'});
7794
 * 
7795
 * tabBox.add(tab1, tab2);
7796
 * (end)
7797
 *
7798
 * License: 
7799
 * Copyright (c) 2008, DM Solutions Group Inc.
7800
 * 
7801
 * This file is licensed under an MIT style license
7802
 */
7803
Jx.TabBox = new Class({
7804
    Family: 'Jx.TabBox',
7805
    Implements: [Options, Events, Jx.Addable],
7806
    options: {
7807
        /* Option: parent
7808
         * a DOM element to add the tab box to
7809
         */
7810
        parent: null,
7811
        /* Option: position
7812
         * the position of the tab bar in the box, one of 'top', 'right',
7813
         * 'bottom' or 'left'.  Top by default.
7814
         */
7815
        position: 'top',
7816
        /* Option: height
7817
         * a fixed height in pixels for the tab box.  If not set, it will fill
7818
         * its container
7819
         */
7820
        height: null,
7821
        /* Option: width
7822
         * a fixed width in pixels for the tab box.  If not set, it will fill
7823
         * its container
7824
         */
7825
        width: null,
7826
        /* Option: scroll
7827
         * should the tab bar scroll its tabs if there are too many to fit
7828
         * in the toolbar, true by default
7829
         */
7830
        scroll:true
7831
    },
7832
    
7833
    /**
7834
     * Property: tabBar
7835
     * {<Jx.Toolbar>} the toolbar for this tab box.
7836
     */
7837
    tabBar: null,
7838
    /**
7839
     * Property: tabSet
7840
     * {<Jx.TabSet>} the tab set for this tab box.
7841
     */
7842
    tabSet: null,
7843
    /**
7844
     * Constructor: Jx.TabBox
7845
     * Create a new instance of a TabBox.
7846
     *
7847
     * Parameters:
7848
     * options - <Jx.TabBox.Options>
7849
     */
7850
    initialize : function(options) {
7851
        this.setOptions(options);
7852
        this.tabBar = new Jx.Toolbar({
7853
            type: 'TabBar', 
7854
            position: this.options.position,
7855
            scroll: this.options.scroll
7856
        });
7857
        this.panel = new Jx.Panel({
7858
            toolbars: [this.tabBar],
7859
            hideTitle: true,
7860
            height: this.options.height,
7861
            width: this.options.width
7862
        });
7863
        this.panel.domObj.addClass('jxTabBox');
7864
        this.tabSet = new Jx.TabSet(this.panel.content);
7865
        this.tabSet.addEvent('tabChange', function(tabSet, tab) {
7866
            this.showItem(tab);
7867
        }.bind(this.tabBar));
7868
        this.domObj = this.panel.domObj;
7869
        /* when the panel changes size, the tab set needs to update 
7870
         * the content areas.
7871
         */
7872
         this.panel.addEvent('sizeChange', (function() {
7873
             this.tabSet.resizeTabBox();
7874
             this.tabBar.domObj.getParent('.jxBarContainer').retrieve('jxBarContainer').update();
7875
         }).bind(this));
7876
        /* when tabs are added or removed, we might need to layout
7877
         * the panel if the toolbar is or becomes empty
7878
         */
7879
        this.tabBar.addEvents({
7880
            add: (function() {
7881
                this.domObj.resize({forceResize: true});
7882
            }).bind(this),
7883
            remove: (function() {
7884
                this.domObj.resize({forceResize: true});
7885
            }).bind(this)
7886
        });
7887
        /* trigger an initial resize when first added to the DOM */
7888
        this.addEvent('addTo', function() {
7889
            this.domObj.resize({forceResize: true});
7890
        });
7891
        if (this.options.parent) {
7892
            this.addTo(this.options.parent);
7893
        }
7894
    },
7895
    /**
7896
     * Method: add
7897
     * Add one or more <Jx.Tab>s to the TabBox.
7898
     *
7899
     * Parameters:
7900
     * tab - {<Jx.Tab>} an instance of <Jx.Tab> to add to the tab box.  More
7901
     * than one tab can be added by passing extra parameters to this method.
7902
     * Unlike <Jx.TabSet>, tabs do not have to be added to a separate 
7903
     * <Jx.Toolbar>.
7904
     */
7905
    add : function() { 
7906
        this.tabBar.add.apply(this.tabBar, arguments); 
7907
        this.tabSet.add.apply(this.tabSet, arguments);
7908
        $A(arguments).flatten().each(function(tab){
7909
            tab.addEvents({
7910
                close: (function(){
7911
                    this.tabBar.remove(tab);
7912
                    this.tabSet.remove(tab);
7913
                }).bind(this)
7914
            });
7915
        }, this);
7916
        return this;
7917
    },
7918
    /**
7919
     * Method: remove
7920
     * Remove a tab from the TabSet.
7921
     *
7922
     * Parameters:
7923
     * tab - {<Jx.Tab>} the tab to remove.
7924
     */
7925
    remove : function(tab) {
7926
        this.tabBar.remove(tab);
7927
        this.tabSet.remove(tab);
7928
    }
7929
});
7930
// $Id: container.js 423 2009-05-12 12:37:56Z pagameba $
7931
/**
7932
 * Class: Jx.Toolbar.Container
7933
 *
7934
 * Extends: Object
7935
 *
7936
 * Implements: Options, Events, <Jx.Addable>
7937
 *
7938
 * A toolbar container contains toolbars.  A single toolbar container fills
7939
 * the available space horizontally.  Toolbars placed in a toolbar container
7940
 * do not wrap when they exceed the available space.
7941
 *
7942
 * Events:
7943
 * add - fired when one or more toolbars are added to a container
7944
 * remove - fired when one or more toolbars are removed from a container
7945
 *
7946
 * Implements: 
7947
 * Options
7948
 * Events
7949
 * {<Jx.Addable>}
7950
 *
7951
 * License: 
7952
 * Copyright (c) 2008, DM Solutions Group Inc.
7953
 * 
7954
 * This file is licensed under an MIT style license
7955
 */
7956
Jx.Toolbar.Container = new Class({
7957
    Family: 'Jx.Toolbar.Container',
7958
    Implements: [Options,Events, Jx.Addable],
7959
    /**
7960
     * Property: domObj
7961
     * {HTMLElement} the HTML element that the container lives in
7962
     */
7963
    domObj : null,
7964
    options: {
7965
        /* Option: parent
7966
         * a DOM element to add this to
7967
         */
7968
        parent: null,
7969
        /* Option: position
7970
         * the position of the toolbar container in its parent, one of 'top',
7971
         * 'right', 'bottom', or 'left'.  Default is 'top'
7972
         */
7973
        position: 'top',
7974
        /* Option: autoSize
7975
         * automatically size the toolbar container to fill its container.
7976
         * Default is false
7977
         */
7978
        autoSize: false,
7979
        /* Option: scroll
7980
         * Control whether the user can scroll of the content of the
7981
         * container if the content exceeds the size of the container.  
7982
         * Default is true.
7983
         */
7984
        scroll: true
7985
    },
7986
    /**
7987
     * Constructor: Jx.Toolbar.Container
7988
     * Create a new instance of Jx.Toolbar.Container
7989
     *
7990
     * Parameters:
7991
     * options - <Jx.Toolbar.Options>
7992
     */
7993
    initialize : function(options) {
7994
        this.setOptions(options);
7995
        
7996
        var d = $(this.options.parent);
7997
        this.domObj = d || new Element('div');
7998
        this.domObj.addClass('jxBarContainer');
7999
        
8000
        if (this.options.scroll) {
8001
            this.scroller = new Element('div', {'class':'jxBarScroller'});
8002
            this.domObj.adopt(this.scroller);
8003
        }
8004

    
8005
        /* this allows toolbars to add themselves to this bar container
8006
         * once it already exists without requiring an explicit reference
8007
         * to the toolbar container
8008
         */
8009
        this.domObj.store('jxBarContainer', this);
8010
        
8011
        if (['top','right','bottom','left'].contains(this.options.position)) {
8012
            this.domObj.addClass('jxBar' +
8013
                           this.options.position.capitalize());            
8014
        } else {
8015
            this.domObj.addClass('jxBarTop');
8016
            this.options.position = 'top';
8017
        }
8018

    
8019
        if (this.options.scroll && ['top','bottom'].contains(this.options.position)) {
8020
            // make sure we update our size when we get added to the DOM
8021
            this.addEvent('addTo', this.update.bind(this));
8022
            
8023
            //making Fx.Tween optional
8024
            if (typeof Fx != 'undefined' && typeof Fx.Tween != 'undefined'){
8025
                this.scrollFx = scrollFx = new Fx.Tween(this.scroller, {
8026
                    link: 'chain'
8027
                });
8028
            }
8029

    
8030
            this.scrollLeft = new Jx.Button({
8031
                image: Jx.aPixel.src
8032
            }).addTo(this.domObj);
8033
            this.scrollLeft.domObj.addClass('jxBarScrollLeft');
8034
            this.scrollLeft.addEvents({
8035
               click: (function(){
8036
                   var from = this.scroller.getStyle('left').toInt();
8037
                   if (isNaN(from)) { from = 0; }
8038
                   var to = Math.min(from+100, 0);
8039
                   if (to >= 0) {
8040
                       this.scrollLeft.domObj.setStyle('visibility', 'hidden');
8041
                   }
8042
                   this.scrollRight.domObj.setStyle('visibility', '');
8043
                   if ($defined(this.scrollFx)){
8044
                       this.scrollFx.start('left', from, to);
8045
                   } else {
8046
                       this.scroller.setStyle('left',to);
8047
                   }
8048
               }).bind(this)
8049
            });
8050
            
8051
            this.scrollRight = new Jx.Button({
8052
                image: Jx.aPixel.src
8053
            }).addTo(this.domObj);
8054
            this.scrollRight.domObj.addClass('jxBarScrollRight');
8055
            this.scrollRight.addEvents({
8056
               click: (function(){
8057
                   var from = this.scroller.getStyle('left').toInt();
8058
                   if (isNaN(from)) { from = 0; }
8059
                   var to = Math.max(from - 100, this.scrollWidth);
8060
                   if (to == this.scrollWidth) {
8061
                       this.scrollRight.domObj.setStyle('visibility', 'hidden');
8062
                   }
8063
                   this.scrollLeft.domObj.setStyle('visibility', '');
8064
                   if ($defined(this.scrollFx)){
8065
                       this.scrollFx.start('left', from, to);
8066
                   } else {
8067
                       this.scroller.setStyle('left',to);
8068
                   }
8069
               }).bind(this)
8070
            });         
8071
            
8072
        } else {
8073
            this.options.scroll = false;
8074
        }
8075

    
8076
        if (this.options.toolbars) {
8077
            this.add(this.options.toolbars);
8078
        }
8079
    },
8080
    
8081
    update: function() {
8082
        if (this.options.autoSize) {
8083
            /* delay the size update a very small amount so it happens
8084
             * after the current thread of execution finishes.  If the
8085
             * current thread is part of a window load event handler,
8086
             * rendering hasn't quite finished yet and the sizes are
8087
             * all wrong
8088
             */
8089
            (function(){
8090
                var x = 0;
8091
                this.scroller.getChildren().each(function(child){
8092
                    x+= child.getSize().x;
8093
                });
8094
                this.domObj.setStyles({width:x});
8095
                this.measure();
8096
            }).delay(1,this);
8097
        } else {
8098
            this.measure();
8099
        }
8100
    },
8101
    
8102
    measure: function() {
8103
        if ((!this.scrollLeftSize || !this.scrollLeftSize.x) && this.domObj.parentNode) {
8104
            this.scrollLeftSize = this.scrollLeft.domObj.getSize();
8105
            this.scrollRightSize = this.scrollRight.domObj.getSize();
8106
        }
8107
        /* decide if we need to show the scroller buttons and
8108
         * do some calculations that will make it faster
8109
         */
8110
        this.scrollWidth = this.domObj.getSize().x;
8111
        this.scroller.getChildren().each(function(child){
8112
            this.scrollWidth -= child.getSize().x;
8113
        }, this);
8114
        if (this.scrollWidth < 0) {
8115
            /* we need to show scrollers on at least one side */
8116
            var l = this.scroller.getStyle('left').toInt();
8117
            if (l < 0) {
8118
                this.scrollLeft.domObj.setStyle('visibility','');
8119
            } else {
8120
                this.scrollLeft.domObj.setStyle('visibility','hidden');
8121
            }
8122
            if (l <= this.scrollWidth) {
8123
                this.scrollRight.domObj.setStyle('visibility', 'hidden');
8124
                if (l < this.scrollWidth) {
8125
                    if ($defined(this.scrollFx)){
8126
                        this.scrollFx.start('left', l, this.scrollWidth);
8127
                    } else {
8128
                        this.scroller.setStyle('left',this.scrollWidth);
8129
                    }
8130
                }
8131
            } else {
8132
                this.scrollRight.domObj.setStyle('visibility', '');                
8133
            }
8134
            
8135
        } else {
8136
            /* don't need any scrollers but we might need to scroll
8137
             * the toolbar into view
8138
             */
8139
            this.scrollLeft.domObj.setStyle('visibility','hidden');
8140
            this.scrollRight.domObj.setStyle('visibility','hidden');
8141
            var from = this.scroller.getStyle('left').toInt();
8142
            if (!isNaN(from) && from !== 0) {
8143
                if ($defined(this.scrollFx)) {
8144
                    this.scrollFx.start('left', 0);
8145
                } else {
8146
                    this.scroller.setStyle('left',0);
8147
                }
8148
            }
8149
        }            
8150
    },
8151
    
8152
    /**
8153
     * Method: add
8154
     * Add a toolbar to the container.
8155
     *
8156
     * Parameters:
8157
     * toolbar - {Object} the toolbar to add.  More than one toolbar
8158
     *    can be added by passing multiple arguments.
8159
     */
8160
    add: function( ) {
8161
        $A(arguments).flatten().each(function(thing) {
8162
            if (this.options.scroll) {
8163
                /* we potentially need to show or hide scroller buttons
8164
                 * when the toolbar contents change
8165
                 */
8166
                thing.addEvent('add', this.update.bind(this));
8167
                thing.addEvent('remove', this.update.bind(this));                
8168
                thing.addEvent('show', this.scrollIntoView.bind(this));                
8169
            }
8170
            if (this.scroller) {
8171
                this.scroller.adopt(thing.domObj);
8172
            } else {
8173
                this.domObj.adopt(thing.domObj);
8174
            }
8175
            this.domObj.addClass('jx'+thing.options.type+this.options.position.capitalize());
8176
        }, this);
8177
        if (this.options.scroll) {
8178
            this.update();            
8179
        }
8180
        if (arguments.length > 0) {
8181
            this.fireEvent('add', this);
8182
        }
8183
        return this;
8184
    },
8185
    /**
8186
     * Method: remove
8187
     * remove an item from a toolbar.  If the item is not in this toolbar
8188
     * nothing happens
8189
     *
8190
     * Parameters:
8191
     * item - {Object} the object to remove
8192
     *
8193
     * Returns:
8194
     * {Object} the item that was removed, or null if the item was not
8195
     * removed.
8196
     */
8197
    remove: function(item) {
8198
        
8199
    },
8200
    /**
8201
     * Method: scrollIntoView
8202
     * scrolls an item in one of the toolbars into the currently visible
8203
     * area of the container if it is not already fully visible
8204
     *
8205
     * Parameters:
8206
     * item - the item to scroll.
8207
     */
8208
    scrollIntoView: function(item) {
8209
        var width = this.domObj.getSize().x;
8210
        var coords = item.domObj.getCoordinates(this.scroller);
8211
        
8212
        //left may be set to auto or even a zero length string. 
8213
        //In the previous version, in air, this would evaluate to
8214
        //NaN which would cause the right hand scroller to show when 
8215
        //the component was first created.
8216
        
8217
        //So, get the left value first
8218
        var l = this.scroller.getStyle('left');
8219
        //then check to see if it's auto or a zero length string 
8220
        if (l === 'auto' || l.length <= 0) {
8221
            //If so, set to 0.
8222
            l = 0;
8223
        } else {
8224
            //otherwise, convert to int
8225
            l = l.toInt();
8226
        }
8227
        var slSize = this.scrollLeftSize ? this.scrollLeftSize.x : 0;
8228
        var srSize = this.scrollRightSize ? this.scrollRightSize.x : 0;
8229
        
8230
        var left = l;
8231
        if (l < -coords.left + slSize) {
8232
            /* the left edge of the item is not visible */
8233
            left = -coords.left + slSize;
8234
            if (left >= 0) {
8235
                left = 0;
8236
            }
8237
        } else if (width - coords.right - srSize< l) {
8238
            /* the right edge of the item is not visible */
8239
            left =  width - coords.right - srSize;
8240
            if (left < this.scrollWidth) {
8241
                left = this.scrollWidth;
8242
            }
8243
        }
8244
                
8245
        if (left < 0) {
8246
            this.scrollLeft.domObj.setStyle('visibility','');                
8247
        } else {
8248
            this.scrollLeft.domObj.setStyle('visibility','hidden');
8249
        }
8250
        if (left <= this.scrollWidth) {
8251
            this.scrollRight.domObj.setStyle('visibility', 'hidden');
8252
        } else {
8253
            this.scrollRight.domObj.setStyle('visibility', '');                
8254
        }
8255
        if (left != l) {
8256
            if ($defined(this.scrollFx)) {
8257
                this.scrollFx.start('left', left);
8258
            } else {
8259
                this.scroller.setStyle('left',left);
8260
            }
8261
        }
8262
    }
8263
});
8264
// $Id: toolbar.item.js 423 2009-05-12 12:37:56Z pagameba $
8265
/**
8266
 * Class: Jx.Toolbar.Item
8267
 * 
8268
 * Extends: Object
8269
 *
8270
 * Implements: Options
8271
 *
8272
 * A helper class to provide a container for something to go into 
8273
 * a <Jx.Toolbar>.
8274
 *
8275
 * License: 
8276
 * Copyright (c) 2008, DM Solutions Group Inc.
8277
 * 
8278
 * This file is licensed under an MIT style license
8279
 */
8280
Jx.Toolbar.Item = new Class( {
8281
    Family: 'Jx.Toolbar.Item',
8282
    Implements: [Options],
8283
    options: {
8284
        /* Option: active
8285
         * is this item active or not?  Default is true.
8286
         */
8287
        active: true
8288
    },
8289
    /**
8290
     * Property: domObj
8291
     * {HTMLElement} an element to contain the thing to be placed in the
8292
     * toolbar.
8293
     */
8294
    domObj: null,
8295
    /**
8296
     * Constructor: Jx.Toolbar.Item
8297
     * Create a new instance of Jx.Toolbar.Item.
8298
     *
8299
     * Parameters:
8300
     * jxThing - {Object} the thing to be contained.
8301
     */
8302
    initialize : function( jxThing ) {
8303
        this.al = [];
8304
        this.domObj = new Element('li', {'class':'jxToolItem'});
8305
        if (jxThing) {
8306
            if (jxThing.domObj) {
8307
                this.domObj.appendChild(jxThing.domObj);
8308
                if (jxThing instanceof Jx.Button.Tab) {
8309
                    this.domObj.addClass('jxTabItem');
8310
                }
8311
            } else {
8312
                this.domObj.appendChild(jxThing);
8313
                if (jxThing.hasClass('jxTab')) {
8314
                    this.domObj.addClass('jxTabItem');
8315
                }
8316
            }
8317
        }
8318
    }
8319
});// $Id: toolbar.separator.js 423 2009-05-12 12:37:56Z pagameba $
8320
/**
8321
 * Class: Jx.Toolbar.Separator
8322
 *
8323
 * Extends: Object
8324
 *
8325
 * A helper class that represents a visual separator in a <Jx.Toolbar>
8326
 *
8327
 * Example:
8328
 * (code)
8329
 * (end)
8330
 *
8331
 * License: 
8332
 * Copyright (c) 2008, DM Solutions Group Inc.
8333
 * 
8334
 * This file is licensed under an MIT style license
8335
 */
8336
Jx.Toolbar.Separator = new Class({
8337
    Family: 'Jx.Toolbar.Separator',
8338
    /**
8339
     * Property: domObj
8340
     * {HTMLElement} The DOM element that goes in the <Jx.Toolbar>
8341
     */
8342
    domObj: null,
8343
    /**
8344
     * Constructor: Jx.Toolbar.Separator
8345
     * Create a new Jx.Toolbar.Separator
8346
     */
8347
    initialize: function() {
8348
        this.domObj = new Element('li', {'class':'jxToolItem'});
8349
        this.domSpan = new Element('span', {'class':'jxBarSeparator'});
8350
        this.domObj.appendChild(this.domSpan);
8351
    }
8352
});
8353
// $Id: treeitem.js 423 2009-05-12 12:37:56Z pagameba $
8354
/**
8355
 * Class: Jx.TreeItem 
8356
 *
8357
 * Extends: Object
8358
 *
8359
 * Implements: Options, Events
8360
 *
8361
 * An item in a tree.  An item is a leaf node that has no children.
8362
 *
8363
 * Jx.TreeItem supports selection via the click event.  The application 
8364
 * is responsible for changing the style of the selected item in the tree
8365
 * and for tracking selection if that is important.
8366
 *
8367
 * Example:
8368
 * (code)
8369
 * (end)
8370
 *
8371
 * Events:
8372
 * click - triggered when the tree item is clicked
8373
 *
8374
 * Implements:
8375
 * Events - MooTools Class.Extras
8376
 * Options - MooTools Class.Extras
8377
 *
8378
 * License: 
8379
 * Copyright (c) 2008, DM Solutions Group Inc.
8380
 * 
8381
 * This file is licensed under an MIT style license
8382
 */
8383
Jx.TreeItem = new Class ({
8384
    Family: 'Jx.TreeItem',
8385
    Implements: [Options,Events],
8386
    /**
8387
     * Property: domObj
8388
     * {HTMLElement} a reference to the HTML element that is the TreeItem
8389
     * in the DOM
8390
     */
8391
    domObj : null,
8392
    /**
8393
     * Property: owner
8394
     * {Object} the folder or tree that this item belongs to
8395
     */
8396
    owner: null,
8397
    options: {
8398
        /* Option: label
8399
         * {String} the label to display for the TreeItem
8400
         */        
8401
        label: '',
8402
        /* Option: data
8403
         * {Object} any arbitrary data to be associated with the TreeItem
8404
         */
8405
        data: null,
8406
        /* Option: contextMenu
8407
         * {<Jx.ContextMenu>} the context menu to trigger if there
8408
         * is a right click on the node
8409
         */
8410
        contextMenu: null,
8411
        /* Option: enabled
8412
         * {Boolean} the initial state of the TreeItem.  If the 
8413
         * TreeItem is not enabled, it cannot be clicked.
8414
         */
8415
        enabled: true,
8416
        type: 'Item',
8417
        /* Option: image
8418
         * {String} URL to an image to use as the icon next to the
8419
         * label of this TreeItem
8420
         */
8421
        image: null,
8422
        /* Option: imageClass
8423
         * {String} CSS class to apply to the image, useful for using CSS
8424
         * sprites
8425
         */
8426
        imageClass: ''
8427
    },
8428
    /**
8429
     * Constructor: Jx.TreeItem
8430
     * Create a new instance of Jx.TreeItem with the associated options
8431
     *
8432
     * Parameters:
8433
     * options - <Jx.TreeItem.Options>
8434
     */
8435
    initialize : function( options ) {
8436
        this.setOptions(options);
8437

    
8438
        this.domObj = new Element('li', {'class':'jxTree'+this.options.type});
8439
        if (this.options.id) {
8440
            this.domObj.id = this.options.id;
8441
        }
8442
      
8443
        this.domNode = new Element('img',{
8444
            'class': 'jxTreeImage', 
8445
            src: Jx.aPixel.src,
8446
            alt: '',
8447
            title: ''
8448
        });
8449
        this.domObj.appendChild(this.domNode);
8450
        
8451
        this.domLabel = (this.options.draw) ? 
8452
            this.options.draw.apply(this) : 
8453
            this.draw();
8454

    
8455
        this.domObj.appendChild(this.domLabel);
8456
        this.domObj.store('jxTreeItem', this);
8457
        
8458
        if (!this.options.enabled) {
8459
            this.domObj.addClass('jxDisabled');
8460
        }
8461
    },
8462
    draw: function() {
8463
        var domImg = new Element('img',{
8464
            'class':'jxTreeIcon', 
8465
            src: Jx.aPixel.src,
8466
            alt: '',
8467
            title: ''
8468
        });
8469
        if (this.options.image) {
8470
            domImg.setStyle('backgroundImage', 'url('+this.options.image+')');
8471
        }
8472
        if (this.options.imageClass) {
8473
            domImg.addClass(this.options.imageClass);
8474
        }
8475
        // the clickable part of the button
8476
        var hasFocus;
8477
        var mouseDown;
8478
        
8479
        var domA = new Element('a',{
8480
            href:'javascript:void(0)',
8481
            html: this.options.label
8482
        });
8483
        domA.addEvents({
8484
            click: this.selected.bind(this),
8485
            dblclick: this.selected.bind(this),
8486
            drag: function(e) {e.stop();},
8487
            contextmenu: function(e) { e.stop(); },
8488
            mousedown: (function(e) {
8489
               domA.addClass('jxTreeItemPressed');
8490
               hasFocus = true;
8491
               mouseDown = true;
8492
               domA.focus();
8493
               if (e.rightClick && this.options.contextMenu) {
8494
                   this.options.contextMenu.show(e);
8495
               }
8496
            }).bind(this),
8497
            mouseup: function(e) {
8498
                domA.removeClass('jxTreeItemPressed');
8499
                mouseDown = false;
8500
            },
8501
            mouseleave: function(e) {
8502
                domA.removeClass('jxTreeItemPressed');
8503
            },
8504
            mouseenter: function(e) {
8505
                if (hasFocus && mouseDown) {
8506
                    domA.addClass('jxTreeItemPressed');
8507
                }
8508
            },
8509
            keydown: function(e) {
8510
                if (e.key == 'enter') {
8511
                    domA.addClass('jxTreeItemPressed');
8512
                }
8513
            },
8514
            keyup: function(e) {
8515
                if (e.key == 'enter') {
8516
                    domA.removeClass('jxTreeItemPressed');
8517
                }
8518
            },
8519
            blur: function() { hasFocus = false; }
8520
        });
8521
        domA.appendChild(domImg);
8522
        if (typeof Drag != 'undefined') {
8523
            new Drag(domA, {
8524
                onStart: function() {this.stop();}
8525
            });
8526
        }
8527
        return domA;
8528
    },
8529
    /**
8530
     * Method: finalize
8531
     * Clean up the TreeItem and remove all DOM references
8532
     */
8533
    finalize: function() { this.finalizeItem(); },
8534
    /**
8535
     * Method: finalizeItem
8536
     * Clean up the TreeItem and remove all DOM references
8537
     */
8538
    finalizeItem: function() {  
8539
        if (!this.domObj) {
8540
            return;
8541
        }
8542
        //this.domA.removeEvents();
8543
        this.options = null;
8544
        this.domObj.dispose();
8545
        this.domObj = null;
8546
        this.owner = null;
8547
    },
8548
    /**
8549
     * Method: clone
8550
     * Create a clone of the TreeItem
8551
     * 
8552
     * Returns: 
8553
     * {<Jx.TreeItem>} a copy of the TreeItem
8554
     */
8555
    clone : function() {
8556
        return new Jx.TreeItem(this.options);
8557
    },
8558
    /**
8559
     * Method: update
8560
     * Update the CSS of the TreeItem's DOM element in case it has changed
8561
     * position
8562
     *
8563
     * Parameters:
8564
     * shouldDescend - {Boolean} propagate changes to child nodes?
8565
     */
8566
    update : function(shouldDescend) {
8567
        var isLast = (arguments.length > 1) ? arguments[1] : 
8568
                     (this.owner && this.owner.isLastNode(this));
8569
        if (isLast) {
8570
            this.domObj.removeClass('jxTree'+this.options.type);
8571
            this.domObj.addClass('jxTree'+this.options.type+'Last');
8572
        } else {
8573
            this.domObj.removeClass('jxTree'+this.options.type+'Last');
8574
            this.domObj.addClass('jxTree'+this.options.type);
8575
        }
8576
    },
8577
    /**
8578
     * Method: selected
8579
     * Called when the DOM element for the TreeItem is clicked, the
8580
     * node is selected.
8581
     *
8582
     * Parameters:
8583
     * e - {Event} the DOM event
8584
     */
8585
    selected : function(e) {
8586
        this.fireEvent('click', this);
8587
    },
8588
    /**
8589
     * Method: getName
8590
     * Get the label associated with a TreeItem
8591
     *
8592
     * Returns: 
8593
     * {String} the name
8594
     */
8595
    getName : function() { return this.options.label; },
8596
    /**
8597
     * Method: propertyChanged
8598
     * A property of an object has changed, synchronize the state of the 
8599
     * TreeItem with the state of the object
8600
     *
8601
     * Parameters:
8602
     * obj - {Object} the object whose state has changed
8603
     */
8604
    propertyChanged : function(obj) {
8605
        this.options.enabled = obj.isEnabled();
8606
        if (this.options.enabled) {
8607
            this.domObj.removeClass('jxDisabled');
8608
        } else {
8609
            this.domObj.addClass('jxDisabled');
8610
        }
8611
    }
8612
});
8613
// $Id: treefolder.js 423 2009-05-12 12:37:56Z pagameba $
8614
/**
8615
 * Class: Jx.TreeFolder
8616
 * 
8617
 * Extends: <Jx.TreeItem>
8618
 *
8619
 * A Jx.TreeFolder is an item in a tree that can contain other items.  It is
8620
 * expandable and collapsible.
8621
 *
8622
 * Example:
8623
 * (code)
8624
 * (end)
8625
 *
8626
 * Extends:
8627
 * <Jx.TreeItem>
8628
 *
8629
 * License: 
8630
 * Copyright (c) 2008, DM Solutions Group Inc.
8631
 * 
8632
 * This file is licensed under an MIT style license
8633
 */
8634
Jx.TreeFolder = new Class({
8635
    Family: 'Jx.TreeFolder',
8636
    Extends: Jx.TreeItem,
8637
    /**
8638
     * Property: subDomObj
8639
     * {HTMLElement} an HTML container for the things inside the folder
8640
     */
8641
    subDomObj : null,
8642
    /**
8643
     * Property: nodes
8644
     * {Array} an array of references to the javascript objects that are
8645
     * children of this folder
8646
     */
8647
    nodes : null,
8648

    
8649
    options: {
8650
        /* Option: open
8651
         * is the folder open?  false by default.
8652
         */
8653
        open : false
8654
    },
8655
    /**
8656
     * Constructor: Jx.TreeFolder
8657
     * Create a new instance of Jx.TreeFolder
8658
     *
8659
     * Parameters:
8660
     * options - <Jx.TreeFolder.Options> and <Jx.TreeItem.Options>
8661
     */
8662
    initialize : function( options ) {
8663
        this.parent($merge(options,{type:'Branch'}));
8664

    
8665
        $(this.domNode).addEvent('click', this.clicked.bindWithEvent(this));
8666
        this.addEvent('click', this.clicked.bindWithEvent(this));
8667
                
8668
        this.nodes = [];
8669
        this.subDomObj = new Element('ul', {'class':'jxTree'});
8670
        this.domObj.appendChild(this.subDomObj);
8671
        if (this.options.open) {
8672
            this.expand();
8673
        } else {
8674
            this.collapse();
8675
        }
8676
    },
8677
    /**
8678
     * Method: finalize
8679
     * Clean up a TreeFolder.
8680
     */
8681
    finalize: function() {
8682
        this.finalizeFolder();
8683
        this.finalizeItem();
8684
        this.subDomObj.dispose();
8685
        this.subDomObj = null;
8686
    },
8687
    /**
8688
     * Method: finalizeFolder
8689
     * Internal method to clean up folder-related stuff.
8690
     */
8691
    finalizeFolder: function() {
8692
        this.domObj.childNodes[0].removeEvents();
8693
        for (var i=this.nodes.length-1; i>=0; i--) {
8694
            this.nodes[i].finalize();
8695
            this.nodes.pop();
8696
        }
8697
        
8698
    },
8699
    
8700
    /**
8701
     * Method: clone
8702
     * Create a clone of the TreeFolder
8703
     * 
8704
     * Returns: 
8705
     * {<Jx.TreeFolder>} a copy of the TreeFolder
8706
     */
8707
    clone : function() {
8708
        var node = new Jx.TreeFolder(this.options);
8709
        this.nodes.each(function(n){node.append(n.clone());});
8710
        return node;
8711
    },
8712
    /**
8713
     * Method: isLastNode
8714
     * Indicates if a node is the last thing in the folder.
8715
     *
8716
     * Parameters:
8717
     * node - {Jx.TreeItem} the node to check
8718
     *
8719
     * Returns:
8720
     *
8721
     * {Boolean}
8722
     */
8723
    isLastNode : function(node) {
8724
        if (this.nodes.length == 0) {
8725
            return false;
8726
        } else {
8727
            return this.nodes[this.nodes.length-1] == node;
8728
        }
8729
    },
8730
    /**
8731
     * Method: update
8732
     * Update the CSS of the TreeFolder's DOM element in case it has changed
8733
     * position.
8734
     *
8735
     * Parameters:
8736
     * shouldDescend - {Boolean} propagate changes to child nodes?
8737
     */
8738
    update : function(shouldDescend) {
8739
        /* avoid update if not attached to tree yet */
8740
        if (!this.parent) return;
8741
        var isLast = false;
8742
        if (arguments.length > 1) {
8743
            isLast = arguments[1];
8744
        } else {
8745
            isLast = (this.owner && this.owner.isLastNode(this));
8746
        }
8747
        
8748
        var c = 'jxTree'+this.options.type;
8749
        c += isLast ? 'Last' : '';
8750
        c += this.options.open ? 'Open' : 'Closed';
8751
        this.domObj.className = c;
8752
        
8753
        if (isLast) {
8754
            this.subDomObj.className = 'jxTree';
8755
        } else {
8756
            this.subDomObj.className = 'jxTree jxTreeNest';
8757
        }
8758
        
8759
        if (this.nodes && shouldDescend) {
8760
            var that = this;
8761
            this.nodes.each(function(n,i){
8762
                n.update(false, i==that.nodes.length-1);
8763
            });
8764
        }
8765
    },
8766
    /**
8767
     * Method: append
8768
     * append a node at the end of the sub-tree
8769
     *
8770
     * Parameters:
8771
     * node - {Object} the node to append.
8772
     */
8773
    append : function( node ) {
8774
        node.owner = this;
8775
        this.nodes.push(node);
8776
        this.subDomObj.appendChild( node.domObj );
8777
        this.update(true);
8778
        return this;
8779
    },
8780
    /**
8781
     * Method: insert
8782
     * insert a node after refNode.  If refNode is null, insert at beginning
8783
     *
8784
     * Parameters:
8785
     * node - {Object} the node to insert
8786
     * refNode - {Object} the node to insert before
8787
     */
8788
    insert : function( node, refNode ) {
8789
        node.owner = this;
8790
        //if refNode is not supplied, insert at the beginning.
8791
        if (!refNode) {
8792
            this.nodes.unshift(node);
8793
            //sanity check to make sure there is actually something there
8794
            if (this.subDomObj.childNodes.length ==0) {
8795
                this.subDomObj.appendChild(node.domObj);
8796
            } else {
8797
                this.subDomObj.insertBefore(node.domObj, this.subDomObj.childNodes[0]);                
8798
            }
8799
        } else {
8800
            //walk all nodes looking for the ref node.  Track if it actually
8801
            //happens so we can append if it fails.
8802
            var b = false;
8803
            for(var i=0;i<this.nodes.length;i++) {
8804
                if (this.nodes[i] == refNode) {
8805
                    //increment to append after ref node.  If this pushes us
8806
                    //past the end, it'll get appended below anyway
8807
                    i = i + 1;
8808
                    if (i < this.nodes.length) {
8809
                        this.nodes.splice(i, 0, node);
8810
                        this.subDomObj.insertBefore(node.domObj, this.subDomObj.childNodes[i]);
8811
                        b = true;
8812
                        break;
8813
                    }
8814
                }
8815
            }
8816
            //if the node wasn't inserted, it is because refNode didn't exist
8817
            //and so the fallback is to just append the node.
8818
            if (!b) {
8819
                this.nodes.push(node); 
8820
                this.subDomObj.appendChild(node.domObj); 
8821
            }
8822
        }
8823
        this.update(true);
8824
        return this;
8825
    },
8826
    /**
8827
     * Method: remove
8828
     * remove the specified node from the tree
8829
     *
8830
     * Parameters:
8831
     * node - {Object} the node to remove
8832
     */
8833
    remove : function(node) {
8834
        node.owner = null;
8835
        for(var i=0;i<this.nodes.length;i++) {
8836
            if (this.nodes[i] == node) {
8837
                this.nodes.splice(i, 1);
8838
                this.subDomObj.removeChild(this.subDomObj.childNodes[i]);
8839
                break;
8840
            }
8841
        }
8842
        this.update(true);
8843
        return this;
8844
    },
8845
    /**
8846
     * Method: replace
8847
     * Replace a node with another node
8848
     *
8849
     * Parameters:
8850
     * newNode - {Object} the node to put into the tree
8851
     * refNode - {Object} the node to replace
8852
     *
8853
     * Returns:
8854
     * {Boolean} true if the replacement was successful.
8855
     */
8856
    replace: function( newNode, refNode ) {
8857
        //walk all nodes looking for the ref node. 
8858
        var b = false;
8859
        for(var i=0;i<this.nodes.length;i++) {
8860
            if (this.nodes[i] == refNode) {
8861
                if (i < this.nodes.length) {
8862
                    newNode.owner = this;
8863
                    this.nodes.splice(i, 1, newNode);
8864
                    this.subDomObj.replaceChild(newNode.domObj, refNode.domObj);
8865
                    return true;
8866
                }
8867
            }
8868
        }
8869
        return false;
8870
    },
8871
    
8872
    /**
8873
     * Method: clicked
8874
     * handle the user clicking on this folder by expanding or
8875
     * collapsing it.
8876
     *
8877
     * Parameters: 
8878
     * e - {Event} the event object
8879
     */
8880
    clicked : function(e) {
8881
        if (this.options.open) {
8882
            this.collapse();
8883
        } else {
8884
            this.expand();
8885
        }
8886
    },
8887
    /**
8888
     * Method: expand
8889
     * Expands the folder
8890
     */
8891
    expand : function() {
8892
        this.options.open = true;
8893
        this.subDomObj.setStyle('display', 'block');
8894
        this.update(true);
8895
        this.fireEvent('disclosed', this);    
8896
    },
8897
    /**
8898
     * Method: collapse
8899
     * Collapses the folder
8900
     */
8901
    collapse : function() {
8902
        this.options.open = false;
8903
        this.subDomObj.setStyle('display', 'none');
8904
        this.update(true);
8905
        this.fireEvent('disclosed', this);
8906
    },
8907
    /**
8908
     * Method: findChild
8909
     * Get a reference to a child node by recursively searching the tree
8910
     * 
8911
     * Parameters:
8912
     * path - {Array} an array of labels of nodes to search for
8913
     *
8914
     * Returns:
8915
     * {Object} the node or null if the path was not found
8916
     */
8917
    findChild : function(path) {
8918
        //path is empty - we are asking for this node
8919
        if (path.length == 0)
8920
            return this;
8921
        
8922
        //path has only one thing in it - looking for something in this folder
8923
        if (path.length == 1)
8924
        {
8925
            for (var i=0; i<this.nodes.length; i++)
8926
            {
8927
                if (this.nodes[i].getName() == path[0])
8928
                    return this.nodes[i];
8929
            }
8930
            return null;
8931
        }
8932
        //path has more than one thing in it, find a folder and descend into it    
8933
        var childName = path.shift();
8934
        for (var i=0; i<this.nodes.length; i++)
8935
        {
8936
            if (this.nodes[i].getName() == childName && this.nodes[i].findChild)
8937
                return this.nodes[i].findChild(path);
8938
        }
8939
        return null;
8940
    }
8941
});// $Id: tree.js 423 2009-05-12 12:37:56Z pagameba $
8942
/**
8943
 * Class: Jx.Tree
8944
 *
8945
 * Extends: Jx.TreeFolder
8946
 *
8947
 * Implements: <Jx.Addable>
8948
 *
8949
 * Jx.Tree displays hierarchical data in a tree structure of folders and nodes.
8950
 *
8951
 * Example:
8952
 * (code)
8953
 * (end)
8954
 *
8955
 * Extends: <Jx.TreeFolder>
8956
 *
8957
 * License: 
8958
 * Copyright (c) 2008, DM Solutions Group Inc.
8959
 * 
8960
 * This file is licensed under an MIT style license
8961
 */
8962
Jx.Tree = new Class({
8963
    Extends: Jx.TreeFolder,
8964
    Implements: [Jx.Addable],
8965
    Family: 'Jx.Tree',
8966
    /**
8967
     * Constructor: Jx.Tree
8968
     * Create a new instance of Jx.Tree
8969
     *
8970
     * Parameters:
8971
     * options: options for <Jx.Addable>
8972
     */
8973
    initialize : function( options ) {
8974
        this.parent(options);
8975
        this.subDomObj = new Element('ul',{
8976
            'class':'jxTreeRoot'
8977
        });
8978
        
8979
        this.nodes = [];
8980
        this.isOpen = true;
8981
        
8982
        this.addable = this.subDomObj;
8983
        
8984
        if (this.options.parent) {
8985
            this.addTo(this.options.parent);
8986
        }
8987
    },
8988
    
8989
    /**
8990
     * Method: finalize
8991
     * Clean up a Jx.Tree instance
8992
     */
8993
    finalize: function() { 
8994
        this.clear(); 
8995
        this.subDomObj.parentNode.removeChild(this.subDomObj); 
8996
    },
8997
    /**
8998
     * Method: clear
8999
     * Clear the tree of all child nodes
9000
     */
9001
    clear: function() {
9002
        for (var i=this.nodes.length-1; i>=0; i--) {
9003
            this.subDomObj.removeChild(this.nodes[i].domObj);
9004
            this.nodes[i].finalize();
9005
            this.nodes.pop();
9006
        }
9007
    },
9008
    /**
9009
     * Method: update
9010
     * Update the CSS of the Tree's DOM element in case it has changed
9011
     * position
9012
     *
9013
     * Parameters:
9014
     * shouldDescend - {Boolean} propagate changes to child nodes?
9015
     */
9016
    update: function(shouldDescend) {
9017
        var bLast = true;
9018
        if (this.subDomObj)
9019
        {
9020
            if (bLast) {
9021
                this.subDomObj.removeClass('jxTreeNest');
9022
            } else {
9023
                this.subDomObj.addClass('jxTreeNest');
9024
            }
9025
        }
9026
        if (this.nodes && shouldDescend) {
9027
            this.nodes.each(function(n){n.update(false);});
9028
        }
9029
    },
9030
    /**
9031
     * Method: append
9032
     * Append a node at the end of the sub-tree
9033
     * 
9034
     * Parameters:
9035
     * node - {Object} the node to append.
9036
     */
9037
    append: function( node ) {
9038
        node.owner = this;
9039
        this.nodes.push(node);
9040
        this.subDomObj.appendChild( node.domObj );
9041
        this.update(true);
9042
        return this;    
9043
    }
9044
});
9045