WebUI: Use Map instead of Mootools Hash in Torrents table
[qBittorrent.git] / src / webui / www / private / scripts / lib / mocha-0.9.6.js
blob8d4c433fab85a6fd63ed33c3db6edc6f17f059fa
1 /*
3 Script: Core.js
4         MUI - A Web Applications User Interface Framework.
6 Copyright:
7         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
9 License:
10         MIT-style license.
12 Contributors:
13         - Scott F. Frederick
14         - Joel Lindau
16 Note:
17         This documentation is taken directly from the javascript source files. It is built using Natural Docs.
21 var MUI = MochaUI = new Hash({
23         version: '0.9.6 development',
25         options: new Hash({
26                 theme: 'default',
27                 advancedEffects: false, // Effects that require fast browsers and are cpu intensive.
28                 standardEffects: true   // Basic effects that tend to run smoothly.
29         }),
31         path: {
32                 source:  'scripts/source/', // Path to MochaUI source JavaScript
33                 themes:  'themes/',         // Path to MochaUI Themes
34                 plugins: 'plugins/'         // Path to Plugins
35         },
37         // Returns the path to the current theme directory
38         themePath: function(){
39                 return MUI.path.themes + MUI.options.theme + '/';
40         },
42         files: new Hash()
44 });
46 MUI.files[MUI.path.source + 'Core/Core.js'] = 'loaded';
48 MUI.extend({
50         Windows: {
51                 instances: new Hash()
52         },
54         ieSupport: 'excanvas',  // Makes it easier to switch between Excanvas and Moocanvas for testing
56         ieLegacySupport: Browser.Engine.trident && Browser.version < 9,
58         /*
60         Function: updateContent
61                 Replace the content of a window or panel.
63         Arguments:
64                 updateOptions - (object)
66         updateOptions:
67                 element - The parent window or panel.
68                 childElement - The child element of the window or panel receiving the content.
69                 method - ('get', or 'post') The way data is transmitted.
70                 data - (hash) Data to be transmitted
71                 title - (string) Change this if you want to change the title of the window or panel.
72                 content - (string or element) An html loadMethod option.
73                 loadMethod - ('html', 'xhr', or 'iframe')
74                 url - Used if loadMethod is set to 'xhr' or 'iframe'.
75                 scrollbars - (boolean)
76                 padding - (object)
77                 onContentLoaded - (function)
79         */
80         updateContent: function(options){
82                 var options = $extend({
83                         element:      null,
84                         childElement: null,
85                         method:       null,
86                         data:         null,
87                         title:        null,
88                         content:      null,
89                         loadMethod:   null,
90                         url:          null,
91                         scrollbars:   null,
92                         padding:      null,
93                         require:      {},
94                         onContentLoaded: $empty
95                 }, options);
97                 options.require = $extend({
98                         css: [], images: [], js: [], onload: null
99                 }, options.require);
101                 var args = {};
103                 if (!options.element) return;
104                 var element = options.element;
106                 if (MUI.Windows.instances.get(element.id)){
107                         args.recipient = 'window';
108                 }
109                 else {
110                         args.recipient = 'panel';
111                 }
113                 var instance = element.retrieve('instance');
114                 if (options.title) instance.titleEl.set('html', options.title);
116                 var contentEl = instance.contentEl;
117                 args.contentContainer = options.childElement != null ? options.childElement : instance.contentEl;
118                 var contentWrapperEl = instance.contentWrapperEl;
120                 if (!options.loadMethod){
121                         if (!instance.options.loadMethod){
122                                 if (!options.url){
123                                         options.loadMethod = 'html';
124                                 }
125                                 else {
126                                         options.loadMethod = 'xhr';
127                                 }
128                         }
129                         else {
130                                 options.loadMethod = instance.options.loadMethod;
131                         }
132                 }
134                 // Set scrollbars if loading content in main content container.
135                 // Always use 'hidden' for iframe windows
136                 var scrollbars = options.scrollbars || instance.options.scrollbars;
137                 if (args.contentContainer == instance.contentEl) {
138                         contentWrapperEl.setStyles({
139                                 'overflow': scrollbars != false && options.loadMethod != 'iframe' ? 'auto' : 'hidden'
140                         });
141                 }
143                 if (options.padding != null) {
144                         contentEl.setStyles({
145                                 'padding-top': options.padding.top,
146                                 'padding-bottom': options.padding.bottom,
147                                 'padding-left': options.padding.left,
148                                 'padding-right': options.padding.right
149                         });
150                 }
152                 // Remove old content.
153                 if (args.contentContainer == contentEl) {
154                         contentEl.empty().show();
155                         // Panels are not loaded into the padding div, so we remove them separately.
156                         contentEl.getAllNext('.column').destroy();
157                         contentEl.getAllNext('.columnHandle').destroy();
158                 }
160                 args.onContentLoaded = function(){
162                         if (options.require.js.length || typeof options.require.onload == 'function'){
163                                 new MUI.Require({
164                                         js: options.require.js,
165                                         onload: function(){
166                                                 if (!$defined(options.require.onload))
167                                                         return;
168                                                 if (Browser.Engine.presto){
169                                                         options.require.onload.delay(100);
170                                                 }
171                                                 else {
172                                                         options.require.onload();
173                                                 }
174                                                 options.onContentLoaded ? options.onContentLoaded() : instance.fireEvent('onContentLoaded', element);
175                                         }.bind(this)
176                                 });
177                         }
178                         else {
179                                 options.onContentLoaded ? options.onContentLoaded() : instance.fireEvent('onContentLoaded', element);
180                         }
182                 };
184                 if (options.require.css.length || options.require.images.length){
185                         new MUI.Require({
186                                 css: options.require.css,
187                                 images: options.require.images,
188                                 onload: function(){
189                                         this.loadSelect(instance, options, args);
190                                 }.bind(this)
191                         });
192                 }
193                 else {
194                         this.loadSelect(instance, options, args);
195                 }
196         },
198         loadSelect: function(instance, options, args){
200                 // Load new content.
201                 switch(options.loadMethod){
202                         case 'xhr':
203                                 this.updateContentXHR(instance, options, args);
204                                 break;
205                         case 'iframe':
206                                 this.updateContentIframe(instance, options, args);
207                                 break;
208                         case 'html':
209                         default:
210                                 this.updateContentHTML(instance, options, args);
211                                 break;
212                 }
214         },
216         updateContentXHR: function(instance, options, args){
217                 var contentEl = instance.contentEl;
218                 var contentContainer = args.contentContainer;
219                 var onContentLoaded = args.onContentLoaded;
220                 new Request.HTML({
221                         url: options.url,
222                         update: contentContainer,
223                         method: options.method != null ? options.method : 'get',
224                         data: options.data != null ? new Hash(options.data).toQueryString() : '',
225                         evalScripts: instance.options.evalScripts,
226                         evalResponse: instance.options.evalResponse,
227                         onRequest: function(){
228                                 if (args.recipient == 'window' && contentContainer == contentEl){
229                                         instance.showSpinner();
230                                 }
231                                 else if (args.recipient == 'panel' && contentContainer == contentEl && $('spinner')){
232                                         $('spinner').show();
233                                 }
234                         }.bind(this),
235                         onFailure: function(response){
236                                 if (contentContainer == contentEl){
237                                         var getTitle = new RegExp("<title>[\n\r\s]*(.*)[\n\r\s]*</title>", "gmi");
238                                         var error = getTitle.exec(response.responseText);
239                                         if (!error) error = 'Unknown';
240                                         contentContainer.set('html', '<h3>Error: ' + error + '</h3>');
241                                         if (args.recipient == 'window'){
242                                                 instance.hideSpinner();
243                                         }
244                                         else if (args.recipient == 'panel' && $('spinner')){
245                                                 $('spinner').hide();
246                                         }
247                                 }
248                         }.bind(this),
249                         onSuccess: function(){
250                                 if (contentContainer == contentEl){
251                                         if (args.recipient == 'window') instance.hideSpinner();
252                                         else if (args.recipient == 'panel' && $('spinner')) $('spinner').hide();
253                                 }
254                                 Browser.Engine.trident4 ? onContentLoaded.delay(750) : onContentLoaded();
255                         }.bind(this),
256                         onComplete: function(){}.bind(this)
257                 }).send();
258         },
260         updateContentIframe: function(instance, options, args){
261                 var contentEl = instance.contentEl;
262                 var contentContainer = args.contentContainer;
263                 var contentWrapperEl = instance.contentWrapperEl;
264                 var onContentLoaded = args.onContentLoaded;
265                 if ( instance.options.contentURL == '' || contentContainer != contentEl) {
266                         return;
267                 }
268                 instance.iframeEl = new Element('iframe', {
269                         'id': instance.options.id + '_iframe',
270                         'name': instance.options.id + '_iframe',
271                         'class': 'mochaIframe',
272                         'src': options.url,
273                         'marginwidth': 0,
274                         'marginheight': 0,
275                         'frameBorder': 0,
276                         'scrolling': 'auto',
277                         'styles': {
278                                 'height': contentWrapperEl.offsetHeight - contentWrapperEl.getStyle('margin-top').toInt() - contentWrapperEl.getStyle('margin-bottom').toInt(),
279                                 'width': instance.panelEl ? contentWrapperEl.offsetWidth - contentWrapperEl.getStyle('margin-left').toInt() - contentWrapperEl.getStyle('margin-right').toInt() : '100%'
280                         }
281                 }).injectInside(contentEl);
283                 // Add onload event to iframe so we can hide the spinner and run onContentLoaded()
284                 instance.iframeEl.addEvent('load', function(e) {
285                         if (args.recipient == 'window') instance.hideSpinner();
286                         else if (args.recipient == 'panel' && contentContainer == contentEl && $('spinner')) $('spinner').hide();
287                         Browser.Engine.trident4 ? onContentLoaded.delay(50) : onContentLoaded();
288                 }.bind(this));
289                 if (args.recipient == 'window') instance.showSpinner();
290                 else if (args.recipient == 'panel' && contentContainer == contentEl && $('spinner')) $('spinner').show();
291         },
293         updateContentHTML: function(instance, options, args){
294                 var contentEl = instance.contentEl;
295                 var contentContainer = args.contentContainer;
296                 var onContentLoaded = args.onContentLoaded;
297                 var elementTypes = new Array('element', 'textnode', 'whitespace', 'collection');
299                 if (elementTypes.contains($type(options.content))){
300                         options.content.inject(contentContainer);
301                 } else {
302                         contentContainer.set('html', options.content);
303                 }
304                 if (contentContainer == contentEl){
305                         if (args.recipient == 'window') instance.hideSpinner();
306                         else if (args.recipient == 'panel' && $('spinner')) $('spinner').hide();
307                 }
308                 Browser.Engine.trident4 ? onContentLoaded.delay(50) : onContentLoaded();
309         },
311         /*
313         Function: reloadIframe
314                 Reload an iframe. Fixes an issue in Firefox when trying to use location.reload on an iframe that has been destroyed and recreated.
316         Arguments:
317                 iframe - This should be both the name and the id of the iframe.
319         Syntax:
320                 (start code)
321                 MUI.reloadIframe(element);
322                 (end)
324         Example:
325                 To reload an iframe from within another iframe:
326                 (start code)
327                 parent.MUI.reloadIframe('myIframeName');
328                 (end)
330         */
331         reloadIframe: function(iframe){
332                 Browser.Engine.gecko ? $(iframe).src = $(iframe).src : top.frames[iframe].location.reload(true);
333         },
335         roundedRect: function(ctx, x, y, width, height, radius, rgb, a){
336                 ctx.fillStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
337                 ctx.beginPath();
338                 ctx.moveTo(x, y + radius);
339                 ctx.lineTo(x, y + height - radius);
340                 ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
341                 ctx.lineTo(x + width - radius, y + height);
342                 ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
343                 ctx.lineTo(x + width, y + radius);
344                 ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
345                 ctx.lineTo(x + radius, y);
346                 ctx.quadraticCurveTo(x, y, x, y + radius);
347                 ctx.fill();
348         },
350         triangle: function(ctx, x, y, width, height, rgb, a){
351                 ctx.beginPath();
352                 ctx.moveTo(x + width, y);
353                 ctx.lineTo(x, y + height);
354                 ctx.lineTo(x + width, y + height);
355                 ctx.closePath();
356                 ctx.fillStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
357                 ctx.fill();
358         },
360         circle: function(ctx, x, y, diameter, rgb, a){
361                 ctx.beginPath();
362                 ctx.arc(x, y, diameter, 0, Math.PI*2, true);
363                 ctx.fillStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
364                 ctx.fill();
365         },
367         notification: function(message){
368                         new MUI.Window({
369                                 loadMethod: 'html',
370                                 closeAfter: 1500,
371                                 type: 'notification',
372                                 addClass: 'notification',
373                                 content: message,
374                                 width: 220,
375                                 height: 40,
376                                 y: 53,
377                                 padding:  { top: 10, right: 12, bottom: 10, left: 12 },
378                                 shadowBlur: 5
379                         });
380         },
382         /*
384         Function: toggleEffects
385                 Turn effects on and off
387         */
388         toggleAdvancedEffects: function(link){
389                 if (MUI.options.advancedEffects == false) {
390                         MUI.options.advancedEffects = true;
391                         if (link){
392                                 this.toggleAdvancedEffectsLink = new Element('div', {
393                                         'class': 'check',
394                                         'id': 'toggleAdvancedEffects_check'
395                                 }).inject(link);
396                         }
397                 }
398                 else {
399                         MUI.options.advancedEffects = false;
400                         if (this.toggleAdvancedEffectsLink) {
401                                 this.toggleAdvancedEffectsLink.destroy();
402                         }
403                 }
404         },
405         /*
407         Function: toggleStandardEffects
408                 Turn standard effects on and off
410         */
411         toggleStandardEffects: function(link){
412                 if (MUI.options.standardEffects == false) {
413                         MUI.options.standardEffects = true;
414                         if (link){
415                                 this.toggleStandardEffectsLink = new Element('div', {
416                                         'class': 'check',
417                                         'id': 'toggleStandardEffects_check'
418                                 }).inject(link);
419                         }
420                 }
421                 else {
422                         MUI.options.standardEffects = false;
423                         if (this.toggleStandardEffectsLink) {
424                                 this.toggleStandardEffectsLink.destroy();
425                         }
426                 }
427         },
429         /*
431         The underlay is inserted directly under windows when they are being dragged or resized
432         so that the cursor is not captured by iframes or other plugins (such as Flash)
433         underneath the window.
435         */
436         underlayInitialize: function(){
437                 var windowUnderlay = new Element('div', {
438                         'id': 'windowUnderlay',
439                         'styles': {
440                                 'height': parent.getCoordinates().height,
441                                 'opacity': .01,
442                                 'display': 'none'
443                         }
444                 }).inject(document.body);
445         },
446         setUnderlaySize: function(){
447                 $('windowUnderlay').setStyle('height', parent.getCoordinates().height);
448         }
453 function: fixPNG
454         Bob Osola's PngFix for IE6.
456 example:
457         (begin code)
458         <img src="xyz.png" alt="foo" width="10" height="20" onload="fixPNG(this)">
459         (end)
461 note:
462         You must have the image height and width attributes specified in the markup.
466 function fixPNG(myImage){
467         if (Browser.Engine.trident4 && document.body.filters){
468                 var imgID = (myImage.id) ? "id='" + myImage.id + "' " : "";
469                 var imgClass = (myImage.className) ? "class='" + myImage.className + "' " : "";
470                 var imgTitle = (myImage.title) ? "title='" + myImage.title  + "' " : "title='" + myImage.alt + "' ";
471                 var imgStyle = "display:inline-block;" + myImage.style.cssText;
472                 var strNewHTML = "<span " + imgID + imgClass + imgTitle
473                         + " style=\"" + "width:" + myImage.width
474                         + "px; height:" + myImage.height
475                         + "px;" + imgStyle + ";"
476                         + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
477                         + "(src=\'" + myImage.src + "\', sizingMethod='scale');\"></span>";
478                 myImage.outerHTML = strNewHTML;
479         }
482 // Blur all windows if user clicks anywhere else on the page
483 document.addEvent('mousedown', function(event){
484         MUI.blurAll.delay(50);
487 window.addEvent('domready', function(){
488         MUI.underlayInitialize();
491 window.addEvent('resize', function(){
492         if ($('windowUnderlay')) {
493                 MUI.setUnderlaySize();
494         }
495         else {
496                 MUI.underlayInitialize();
497         }
500 Element.implement({
501         hide: function(){
502                 this.setStyle('display', 'none');
503                 return this;
504         },
505         show: function(){
506                 this.setStyle('display', 'block');
507                 return this;
508         }
513 Shake effect by Uvumi Tools
514 http://tools.uvumi.com/element-shake.html
516 Function: shake
518 Example:
519         Shake a window.
520         (start code)
521         $('parametrics').shake()
522         (end)
526 Element.implement({
527         shake: function(radius,duration){
528                 radius = radius || 3;
529                 duration = duration || 500;
530                 duration = (duration/50).toInt() - 1;
531                 var parent = this.getParent();
532                 if(parent != $(document.body) && parent.getStyle('position') == 'static'){
533                         parent.setStyle('position','relative');
534                 }
535                 var position = this.getStyle('position');
536                 if(position == 'static'){
537                         this.setStyle('position','relative');
538                         position = 'relative';
539                 }
540                 if(MUI.ieLegacySupport){
541                         parent.setStyle('height',parent.getStyle('height'));
542                 }
543                 var coords = this.getPosition(parent);
544                 if(position == 'relative' && !Browser.Engine.presto){
545                         coords.x -= parent.getStyle('paddingLeft').toInt();
546                         coords.y -= parent.getStyle('paddingTop').toInt();
547                 }
548                 var morph = this.retrieve('morph');
549                 if (morph){
550                         morph.cancel();
551                         var oldOptions = morph.options;
552                 }
553                 var morph = this.get('morph',{
554                         duration:50,
555                         link:'chain'
556                 });
557                 for(var i=0 ; i < duration ; i++){
558                         morph.start({
559                                 top:coords.y+$random(-radius,radius),
560                                 left:coords.x+$random(-radius,radius)
561                         });
562                 }
563                 morph.start({
564                         top:coords.y,
565                         left:coords.x
566                 }).chain(function(){
567                         if(oldOptions){
568                                 this.set('morph',oldOptions);
569                         }
570                 }.bind(this));
571                 return this;
572         }
575 String.implement({
577         parseQueryString: function() {
578                 var vars = this.split(/[&;]/);
579                 var rs = {};
580                 if (vars.length) vars.each(function(val) {
581                         var keys = val.split('=');
582                         if (keys.length && keys.length == 2) rs[decodeURIComponent(keys[0])] = decodeURIComponent(keys[1]);
583                 });
584                 return rs;
585         }
589 // Mootools Patch: Fixes issues in Safari, Chrome, and Internet Explorer caused by processing text as XML.
590 Request.HTML.implement({
592         processHTML: function(text){
593                 var match = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
594                 text = (match) ? match[1] : text;
595                 var container = new Element('div');
596                 return container.set('html', text);
597         }
603         Examples:
604                 (start code)
605                 getCSSRule('.myRule');
606                 getCSSRule('#myRule');
607                 (end)
610 MUI.getCSSRule = function(selector) {
611         for (var ii = 0; ii < document.styleSheets.length; ii++) {
612                 var mysheet = document.styleSheets[ii];
613                 var myrules = mysheet.cssRules ? mysheet.cssRules : mysheet.rules;
614                 for (i = 0; i < myrules.length; i++){
615                         if (myrules[i].selectorText == selector){
616                                 return myrules[i];
617                         }
618                 }
619         }
620         return false;
623 // This makes it so Request will work to some degree locally
624 if (location.protocol == "file:"){
626         Request.implement({
627                 isSuccess : function(status){
628                         return (status == 0 || (status >= 200) && (status < 300));
629                 }
630         });
632         Browser.Request = function(){
633                 return $try(function(){
634                         return new ActiveXObject('MSXML2.XMLHTTP');
635                 }, function(){
636                         return new XMLHttpRequest();
637                 });
638         };
642 MUI.Require = new Class({
644         Implements: [Options],
646         options: {
647                 css: [],
648                 images: [],
649                 js: [],
650                 onload: $empty
651         },
653         initialize: function(options){
654                 this.setOptions(options);
655                 var options = this.options;
657                 this.assetsToLoad = options.css.length + options.images.length + options.js.length;
658                 this.assetsLoaded = 0;
660                 var cssLoaded = 0;
662                 // Load CSS before images and JavaScript
664                 if (options.css.length){
665                         options.css.each( function(sheet){
667                                 this.getAsset(sheet, function(){
668                                         if (cssLoaded == options.css.length - 1){
670                                                 if (this.assetsLoaded == this.assetsToLoad - 1){
671                                                         this.requireOnload();
672                                                 }
673                                                 else {
674                                                         // Add a little delay since we are relying on cached CSS from XHR request.
675                                                         this.assetsLoaded++;
676                                                         this.requireContinue.delay(50, this);
677                                                 }
678                                         }
679                                         else {
680                                                 cssLoaded++;
681                                                 this.assetsLoaded++;
682                                         }
683                                 }.bind(this));
684                         }.bind(this));
685                 }
686                 else if (!options.js.length && !options.images.length){
687                         this.options.onload();
688                         return true;
689                 }
690                 else {
691                         this.requireContinue.delay(50, this); // Delay is for Safari
692                 }
694         },
696         requireOnload: function(){
697                 this.assetsLoaded++;
698                 if (this.assetsLoaded == this.assetsToLoad){
699                         this.options.onload();
700                         return true;
701                 }
703         },
705         requireContinue: function(){
707                 var options = this.options;
708                 if (options.images.length){
709                         options.images.each( function(image){
710                                 this.getAsset(image, this.requireOnload.bind(this));
711                         }.bind(this));
712                 }
714                 if (options.js.length){
715                         options.js.each( function(script){
716                                 this.getAsset(script, this.requireOnload.bind(this));
717                         }.bind(this));
718                 }
720         },
722         getAsset: function(source, onload){
724                 // If the asset is loaded, fire the onload function.
725                 if ( MUI.files[source] == 'loaded' ){
726                         if (typeof onload == 'function'){
727                                 onload();
728                         }
729                         return true;
730                 }
732                 // If the asset is loading, wait until it is loaded and then fire the onload function.
733                 // If asset doesn't load by a number of tries, fire onload anyway.
734                 else if ( MUI.files[source] == 'loading' ){
735                         var tries = 0;
736                         var checker = (function(){
737                                 tries++;
738                                 if (MUI.files[source] == 'loading' && tries < '100') return;
739                                 $clear(checker);
740                                 if (typeof onload == 'function'){
741                                         onload();
742                                 }
743                         }).periodical(50);
744                 }
746                 // If the asset is not yet loaded or loading, start loading the asset.
747                 else {
748                         MUI.files[source] = 'loading';
750                         properties = {
751                                 'onload': onload != 'undefined' ? onload : $empty
752                         };
754                         // Add to the onload function
755                         var oldonload = properties.onload;
756                         properties.onload = function() {
757                                 MUI.files[source] = 'loaded';
758                                 if (oldonload) {
759                                                 oldonload();
760                                 }
761                         }.bind(this);
763                         switch ( source.match(/\.\w+$/)[0] ) {
764                                 case '.js': return Asset.javascript(source, properties);
765                                 case '.css': return Asset.css(source, properties);
766                                 case '.jpg':
767                                 case '.png':
768                                 case '.gif': return Asset.image(source, properties);
769                         }
771                         alert('The required file "' + source + '" could not be loaded');
772                 }
773         }
777 $extend(Asset, {
779         /* Fix an Opera bug in Mootools 1.2 */
780         javascript: function(source, properties){
781                 properties = $extend({
782                         onload: $empty,
783                         document: document,
784                         check: $lambda(true)
785                 }, properties);
787                 if ($(properties.id)) {
788                         properties.onload();
789                         return $(properties.id);
790                 }
792                 var script = new Element('script', {'src': source, 'type': 'text/javascript'});
794                 var load = properties.onload.bind(script), check = properties.check, doc = properties.document;
795                 delete properties.onload; delete properties.check; delete properties.document;
797                 if (!Browser.Engine.webkit419 && !Browser.Engine.presto){
798                         script.addEvents({
799                                 load: load,
800                                 readystatechange: function(){
801                                         if (MUI.ieLegacySupport && ['loaded', 'complete'].contains(this.readyState))
802                                                 load();
803                                 }
804                         }).setProperties(properties);
805                 }
806                 else {
807                         var checker = (function(){
808                                 if (!$try(check)) return;
809                                 $clear(checker);
810                                 // Opera has difficulty with multiple scripts being injected into the head simultaneously. We need to give it time to catch up.
811                                 Browser.Engine.presto ? load.delay(500) : load();
812                         }).periodical(50);
813                 }
814                 return script.inject(doc.head);
815         },
817         // Get the CSS with XHR before appending it to document.head so that we can have an onload callback.
818         css: function(source, properties){
820                 properties = $extend({
821                         id: null,
822                         media: 'screen',
823                         onload: $empty
824                 }, properties);
826                 new Request({
827                         method: 'get',
828                         url: source,
829                         onComplete: function(response) {
830                                 var newSheet = new Element('link', {
831                                         'id': properties.id,
832                                         'rel': 'stylesheet',
833                                         'media': properties.media,
834                                         'type': 'text/css',
835                                         'href': source
836                                 }).inject(document.head);
837                                 properties.onload();
838                         }.bind(this),
839                         onFailure: function(response){
840                         },
841                         onSuccess: function(){
842                         }.bind(this)
843                 }).send();
844         }
850 REGISTER PLUGINS
852         Register Components and Plugins for Lazy Loading
854         How this works may take a moment to grasp. Take a look at MUI.Window below.
855         If we try to create a new Window and Window.js has not been loaded then the function
856         below will run. It will load the CSS required by the MUI.Window Class and then
857         then it will load Window.js. Here is the interesting part. When Window.js loads,
858         it will overwrite the function below, and new MUI.Window(arg) will be ran
859         again. This time it will create a new MUI.Window instance, and any future calls
860         to new MUI.Window(arg) will immediately create new windows since the assets
861         have already been loaded and our temporary function below has been overwritten.
863         Example:
865         MyPlugins.extend({
867                 MyGadget: function(arg){
868                         new MUI.Require({
869                                 css: [MUI.path.plugins + 'myGadget/css/style.css'],
870                                 images: [MUI.path.plugins + 'myGadget/images/background.gif']
871                                 js: [MUI.path.plugins + 'myGadget/scripts/myGadget.js'],
872                                 onload: function(){
873                                         new MyPlguins.MyGadget(arg);
874                                 }
875                         });
876                 }
878         });
880 -------------------------------------------------------------------- */
882 MUI.extend({
884         newWindowsFromJSON: function(arg){
885                 new MUI.Require({
886                         js: [MUI.path.source + 'Window/Windows-from-json.js'],
887                         onload: function(){
888                                 new MUI.newWindowsFromJSON(arg);
889                         }
890                 });
891         },
893         arrangeCascade: function(){
894                 new MUI.Require({
895                         js: [MUI.path.source + 'Window/Arrange-cascade.js'],
896                         onload: function(){
897                                 new MUI.arrangeCascade();
898                         }
899                 });
900         },
902         arrangeTile: function(){
903                 new MUI.Require({
904                         js: [MUI.path.source + 'Window/Arrange-tile.js'],
905                         onload: function(){
906                                 new MUI.arrangeTile();
907                         }
908                 });
909         },
911         saveWorkspace: function(){
912                 new MUI.Require({
913                         js: [MUI.path.source + 'Layout/Workspaces.js'],
914                         onload: function(){
915                                 new MUI.saveWorkspace();
916                         }
917                 });
918         },
920         loadWorkspace: function(){
921                 new MUI.Require({
922                         js: [MUI.path.source + 'Layout/Workspaces.js'],
923                         onload: function(){
924                                 new MUI.loadWorkspace();
925                         }
926                 });
927         },
929         Themes: {
930                 init: function(arg){
931                         new MUI.Require({
932                                 js: [MUI.path.source + 'Utilities/Themes.js'],
933                                 onload: function(){
934                                         MUI.Themes.init(arg);
935                                 }
936                         });
937                 }
938         }
943 Script: Themes.js
944         Allows for switching themes dynamically.
946 Copyright:
947         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
949 License:
950         MIT-style license.
952 Requires:
953         Core.js
955 Notes:
956         Themes are new and experimental.
958 Syntax:
959         (start code)
960         new MUI.Themes.init(newTheme);
961         (end)
963 Example:
964         (start code)
965         new MUI.Themes.init('charcoal');
966         (end)
968 Arguments:
969         newTheme - (string) The theme name
973 MUI.files[MUI.path.source + 'Utilities/Themes.js'] = 1;
975 MUI.Themes = {
977         /*
979         Function: themeInit
980                 Initialize a theme. This is experimental and not fully implemented yet.
982         */
983         init: function(newTheme){
984                 this.newTheme = newTheme.toLowerCase();
985                 if (!this.newTheme || this.newTheme == null || this.newTheme == MUI.options.theme.toLowerCase()) return;
987                 if ($('spinner')) $('spinner').show();
989                 this.oldURIs = [];
990                 this.oldSheets = [];
992                 $$('link').each( function(link){
993                                 var href = link.get('href');
994                                 if (href.contains(MUI.path.themes + MUI.options.theme)){
995                                         this.oldURIs.push(href);
996                                         this.oldSheets.push(link);
997                                 }
998                 }.bind(this));
1000                 /*
1001                 MUI.files.each( function(value, key, hash){
1002                         if (key.contains(MUI.path.themes + MUI.options.theme)){
1003                                 this.oldURIs.push(key);
1004                         }
1005                 }.bind(this));
1006                 */
1008                 this.newSheetURLs = this.oldURIs.map(function(item, index){
1009                 return item.replace("/" + MUI.options.theme + "/", "/" + MUI.Themes.newTheme + "/");
1010                 }.bind(this));
1012                 this.sheetsToLoad = this.oldURIs.length;
1013                 this.sheetsLoaded = 0;
1015                 // Download new stylesheets and add them to an array
1016                 this.newSheets = [];
1017                 this.newSheetURLs.each( function(link){
1018                         var href = link;
1020                                 //var id = link.id;
1022                                 var cssRequest = new Request({
1023                                         method: 'get',
1024                                         url: href,
1025                                         onComplete: function(response) {
1026                                                 var newSheet = new Element('link', {
1027                                                         //'id': id,
1028                                                         'rel': 'stylesheet',
1029                                                         'media': 'screen',
1030                                                         'type': 'text/css',
1031                                                         'href': href
1032                                                 });
1033                                                 this.newSheets.push(newSheet);
1034                                         }.bind(this),
1035                                         onFailure: function(response){
1036                                                 this.themeLoadSuccess = false;
1037                                                 if ($('spinner')) $('spinner').hide();
1038                                                 MUI.notification('Stylesheets did not load.');
1039                                         },
1040                                         onSuccess: function(){
1041                                                 this.sheetsLoaded++;
1042                                                 if (this.sheetsLoaded == this.sheetsToLoad) {
1043                                                         this.updateThemeStylesheets();
1044                                                         this.themeLoadSuccess = true;
1045                                                 }
1046                                         }.bind(this)
1047                                 });
1048                                 cssRequest.send();
1050                 }.bind(this));
1052         },
1053         updateThemeStylesheets: function(){
1055                 this.oldSheets.each( function(sheet){
1056                         sheet.destroy();
1057                 });
1059                 this.newSheets.each( function(sheet){
1060                         MUI.files[sheet.get('href')] = 1;
1061                         sheet.inject(document.head);
1062                 });
1064                 // Delay gives the stylesheets time to take effect. IE6 needs more delay.
1065                 if (MUI.ieLegacySupport){
1066                         this.redraw.delay(1250, this);
1067                 }
1068                 else {
1069                         this.redraw.delay(250, this);
1070                 }
1072         },
1073         redraw: function(){
1075                 $$('.replaced').removeClass('replaced');
1077                 // Redraw open windows
1078                 $$('.mocha').each( function(element){
1079                         var instance = element.retrieve('instance');
1081                         // Convert CSS colors to Canvas colors.
1082                         instance.setColors();
1083                         instance.drawWindow();
1084                 });
1086                 if (MUI.Dock){
1087                         if (MUI.Dock.options.useControls){
1088                                 MUI.Dock.setDockColors();
1089                                 MUI.Dock.renderDockControls();
1090                         }
1091                 }
1093                 // Reformat layout
1094                 if (MUI.Desktop.desktop){
1095                         var checker = (function(){
1096                                 // Make sure the style sheets are really ready.
1097                                 if (MUI.Desktop.desktop.getStyle('overflow') != 'hidden'){
1098                                         return;
1099                                 }
1100                                 $clear(checker);
1101                                 MUI.Desktop.setDesktopSize();
1102                         }).periodical(50);
1103                 }
1105                 if ($('spinner')) $('spinner').hide();
1106                 MUI.options.theme = this.newTheme;
1108                 /*
1109                 this.cookie = new Hash.Cookie('mochaUIthemeCookie', {duration: 3600});
1110                 this.cookie.empty();
1111                 this.cookie.set('theme', MUI.options.theme);
1112                 this.cookie.save();
1113                 */
1115         }
1118 window.addEvent('load', function(){
1119         /*
1120         // Load theme the user was last using. This needs work.
1121         var cookie = new Hash.Cookie('mochaUIthemeCookie', {duration: 3600});
1122         var themeCookie = cookie.load();
1123         if(cookie.getKeys().length){
1124                 if (themeCookie.get('theme') != MUI.Themes.options.theme){
1125                         MUI.Themes.init.delay(1000, MUI.Themes, themeCookie.get('theme'));
1126                 }
1127         }
1128         */
1130         if ($('themeControl')){
1131                 $('themeControl').getElements('option').setProperty('selected', 'false');
1132                 if ($('chooseTheme')){
1133                         $('chooseTheme').setProperty('selected', 'true');
1134                 }
1135         }
1139 Script: Window.js
1140         Build windows.
1142 Copyright:
1143         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
1145 License:
1146         MIT-style license.
1148 Requires:
1149         Core.js
1153 MUI.files[MUI.path.source + 'Window/Window.js'] = 'loading';
1154 //$require(MUI.themePath() + '/css/Dock.css');
1157 Class: Window
1158         Creates a single MochaUI window.
1160 Syntax:
1161         (start code)
1162         new MUI.Window(options);
1163         (end)
1165 Arguments:
1166         options
1168 Options:
1169         id - The ID of the window. If not defined, it will be set to 'win' + windowIDCount.
1170         title - The title of the window.
1171         icon - Place an icon in the window's titlebar. This is either set to false or to the url of the icon. It is set up for icons that are 16 x 16px.
1172         type - ('window', 'modal', 'modal2', or 'notification') Defaults to 'window'. Modals should be created with new MUI.Modal(options).
1173         loadMethod - ('html', 'xhr', or 'iframe') Defaults to 'html' if there is no contentURL. Defaults to 'xhr' if there is a contentURL. You only really need to set this if using the 'iframe' method.
1174         contentURL - Used if loadMethod is set to 'xhr' or 'iframe'.
1175         closeAfter - Either false or time in milliseconds. Closes the window after a certain period of time in milliseconds. This is particularly useful for notifications.
1176         evalScripts - (boolean) An xhr loadMethod option. Defaults to true.
1177         evalResponse - (boolean) An xhr loadMethod option. Defaults to false.
1178         content - (string or element) An html loadMethod option.
1179         toolbar - (boolean) Create window toolbar. Defaults to false. This can be used for tabs, media controls, and so forth.
1180         toolbarPosition - ('top' or 'bottom') Defaults to top.
1181         toolbarHeight - (number)
1182         toolbarURL - (url) Defaults to 'pages/lipsum.html'.
1183         toolbarContent - (string)
1184         toolbarOnload - (function)
1185         toolbar2 - (boolean) Create window toolbar. Defaults to false. This can be used for tabs, media controls, and so forth.
1186         toolbar2Position - ('top' or 'bottom') Defaults to top.
1187         toolbar2Height - (number)
1188         toolbar2URL - (url) Defaults to 'pages/lipsum.html'.
1189         toolbar2Content - (string)
1190         toolbar2Onload - (function)
1191         container - (element ID) Element the window is injected in. The container defaults to 'desktop'. If no desktop then to document.body. Use 'pageWrapper' if you don't want the windows to overlap the toolbars.
1192         restrict - (boolean) Restrict window to container when dragging.
1193         shape - ('box' or 'gauge') Shape of window. Defaults to 'box'.
1194         collapsible - (boolean) Defaults to true.
1195         minimizable - (boolean) Requires MUI.Desktop and MUI.Dock. Defaults to true if dependenices are met.
1196         maximizable - (boolean) Requires MUI.Desktop. Defaults to true if dependenices are met.
1197         closable - (boolean) Defaults to true.
1198         storeOnClose - (boolean) Hides a window and it's dock tab rather than destroying them on close. If you try to create the window again it will unhide the window and dock tab.
1199         modalOverlayClose - (boolean) Whether or not you can close a modal by clicking on the modal overlay. Defaults to true.
1200         draggable - (boolean) Defaults to false for modals; otherwise true.
1201         draggableGrid - (false or number) Distance in pixels for snap-to-grid dragging. Defaults to false.
1202         draggableLimit - (false or number) An object with x and y properties used to limit the movement of the Window. Defaults to false.
1203         draggableSnap - (boolean) The distance to drag before the Window starts to respond to the drag. Defaults to false.
1204         resizable - (boolean) Defaults to false for modals, notifications and gauges; otherwise true.
1205         resizeLimit - (object) Minimum and maximum width and height of window when resized.
1206         addClass - (string) Add a class to the window for more control over styling.
1207         width - (number) Width of content area.
1208         height - (number) Height of content area.
1209         headerHeight - (number) Height of window titlebar.
1210         footerHeight - (number) Height of window footer.
1211         cornerRadius - (number)
1212         x - (number) If x and y are left undefined the window is centered on the page.
1213         y - (number)
1214         scrollbars - (boolean)
1215         padding - (object)
1216         shadowBlur - (number) Width of shadows.
1217         shadowOffset - Should be positive and not be greater than the ShadowBlur.
1218         controlsOffset - Change this if you want to reposition the window controls.
1219         useCanvas - (boolean) Set this to false if you don't want a canvas body.
1220         useCanvasControls - (boolean) Set this to false if you wish to use images for the buttons.
1221         useSpinner - (boolean) Toggles whether or not the ajax spinners are displayed in window footers. Defaults to true.
1222         headerStartColor - ([r,g,b,]) Titlebar gradient's top color
1223         headerStopColor - ([r,g,b,]) Titlebar gradient's bottom color
1224         bodyBgColor - ([r,g,b,]) Background color of the main canvas shape
1225         minimizeBgColor - ([r,g,b,]) Minimize button background color
1226         minimizeColor - ([r,g,b,]) Minimize button color
1227         maximizeBgColor - ([r,g,b,]) Maximize button background color
1228         maximizeColor - ([r,g,b,]) Maximize button color
1229         closeBgColor - ([r,g,b,]) Close button background color
1230         closeColor - ([r,g,b,]) Close button color
1231         resizableColor - ([r,g,b,]) Resizable icon color
1232         onBeforeBuild - (function) Fired just before the window is built.
1233         onContentLoaded - (function) Fired when content is successfully loaded via XHR or Iframe.
1234         onFocus - (function)  Fired when the window is focused.
1235         onBlur - (function) Fired when window loses focus.
1236         onResize - (function) Fired when the window is resized.
1237         onMinimize - (function) Fired when the window is minimized.
1238         onMaximize - (function) Fired when the window is maximized.
1239         onRestore - (function) Fired when a window is restored from minimized or maximized.
1240         onClose - (function) Fired just before the window is closed.
1241         onCloseComplete - (function) Fired after the window is closed.
1243 Returns:
1244         Window object.
1246 Example:
1247         Define a window. It is suggested you name the function the same as your window ID + "Window".
1248         (start code)
1249         var mywindowWindow = function(){
1250                 new MUI.Window({
1251                         id: 'mywindow',
1252                         title: 'My Window',
1253                         loadMethod: 'xhr',
1254                         contentURL: 'pages/lipsum.html',
1255                         width: 340,
1256                         height: 150
1257                 });
1258         }
1259         (end)
1261 Example:
1262         Create window onDomReady.
1263         (start code)
1264         window.addEvent('domready', function(){
1265                 mywindow();
1266         });
1267         (end)
1269 Example:
1270         Add link events to build future windows. It is suggested you give your anchor the same ID as your window + "WindowLink" or + "WindowLinkCheck". Use the latter if it is a link in the menu toolbar.
1272         If you wish to add links in windows that open other windows remember to add events to those links when the windows are created.
1274         (start code)
1275         // Javascript:
1276         if ($('mywindowLink')){
1277                 $('mywindowLink').addEvent('click', function(e) {
1278                         new Event(e).stop();
1279                         mywindow();
1280                 });
1281         }
1283         // HTML:
1284         <a id="mywindowLink" href="pages/lipsum.html">My Window</a>
1285         (end)
1288         Loading Content with an XMLHttpRequest(xhr):
1289                 For content to load via xhr all the files must be online and in the same domain. If you need to load content from another domain or wish to have it work offline, load the content in an iframe instead of using the xhr option.
1291         Iframes:
1292                 If you use the iframe loadMethod your iframe will automatically be resized when the window it is in is resized. If you want this same functionality when using one of the other load options simply add class="mochaIframe" to those iframes and they will be resized for you as well.
1296 // Having these options outside of the Class allows us to add, change, and remove
1297 // individual options without rewriting all of them.
1299 MUI.extend({
1300         Windows: {
1301                 instances:      new Hash(),
1302                 indexLevel:     100,          // Used for window z-Index
1303                 windowIDCount:  0,            // Used for windows without an ID defined by the user
1304                 windowsVisible: true,         // Ctrl-Alt-Q to toggle window visibility
1305                 focusingWindow: false
1306         }
1309 MUI.Windows.windowOptions = {
1310         id:                null,
1311         title:             'New Window',
1312         icon:              false,
1313         type:              'window',
1314         require:           {
1315                 css:           [],
1316                 images:        [],
1317                 js:            [],
1318                 onload:        null
1319         },
1320         loadMethod:        null,
1321         method:            'get',
1322         contentURL:        null,
1323         data:              null,
1325         closeAfter:        false,
1327         // xhr options
1328         evalScripts:       true,
1329         evalResponse:      false,
1331         // html options
1332         content:           'Window content',
1334         // Toolbar
1335         toolbar:           false,
1336         toolbarPosition:   'top',
1337         toolbarHeight:     29,
1338         toolbarURL:        'pages/lipsum.html',
1339         toolbarData:       null,
1340         toolbarContent:    '',
1341         toolbarOnload:     $empty,
1343         // Toolbar
1344         toolbar2:           false,
1345         toolbar2Position:   'bottom',
1346         toolbar2Height:     29,
1347         toolbar2URL:        'pages/lipsum.html',
1348         toolbar2Data:       null,
1349         toolbar2Content:    '',
1350         toolbar2Onload:     $empty,
1352         // Container options
1353         container:         null,
1354         restrict:          true,
1355         shape:             'box',
1357         // Window Controls
1358         collapsible:       true,
1359         minimizable:       true,
1360         maximizable:       true,
1361         closable:          true,
1363         // Close options
1364         storeOnClose:       false,
1366         // Modal options
1367         modalOverlayClose: true,
1369         // Draggable
1370         draggable:         null,
1371         draggableGrid:     false,
1372         draggableLimit:    false,
1373         draggableSnap:     false,
1375         // Resizable
1376         resizable:         null,
1377         resizeLimit:       {'x': [250, 2500], 'y': [125, 2000]},
1379         // Style options:
1380         addClass:          '',
1381         width:             300,
1382         height:            125,
1383         headerHeight:      25,
1384         footerHeight:      25,
1385         cornerRadius:      8,
1386         x:                 null,
1387         y:                 null,
1388         scrollbars:        true,
1389         padding:                   { top: 10, right: 12, bottom: 10, left: 12 },
1390         shadowBlur:        5,
1391         shadowOffset:      {'x': 0, 'y': 1},
1392         controlsOffset:    {'right': 6, 'top': 6},
1393         useCanvas:         true,
1394         useCanvasControls: true,
1395         useSpinner:        true,
1397         // Color options:
1398         headerStartColor:  [250, 250, 250],
1399         headerStopColor:   [229, 229, 229],
1400         bodyBgColor:       [229, 229, 229],
1401         minimizeBgColor:   [255, 255, 255],
1402         minimizeColor:     [0, 0, 0],
1403         maximizeBgColor:   [255, 255, 255],
1404         maximizeColor:     [0, 0, 0],
1405         closeBgColor:      [255, 255, 255],
1406         closeColor:        [0, 0, 0],
1407         resizableColor:    [254, 254, 254],
1409         // Events
1410         onBeforeBuild:     $empty,
1411         onContentLoaded:   $empty,
1412         onFocus:           $empty,
1413         onBlur:            $empty,
1414         onResize:          $empty,
1415         onMinimize:        $empty,
1416         onMaximize:        $empty,
1417         onRestore:         $empty,
1418         onClose:           $empty,
1419         onCloseComplete:   $empty
1422 MUI.Windows.windowOptionsOriginal = $merge(MUI.Windows.windowOptions);
1424 MUI.Window = new Class({
1426         Implements: [Events, Options],
1428         options: MUI.Windows.windowOptions,
1430         initialize: function(options){
1431                 this.setOptions(options);
1433                 // Shorten object chain
1434                 var options = this.options;
1436                 $extend(this, {
1437                         mochaControlsWidth: 0,
1438                         minimizebuttonX:  0,  // Minimize button horizontal position
1439                         maximizebuttonX: 0,  // Maximize button horizontal position
1440                         closebuttonX: 0,  // Close button horizontal position
1441                         headerFooterShadow: options.headerHeight + options.footerHeight + (options.shadowBlur * 2),
1442                         oldTop: 0,
1443                         oldLeft: 0,
1444                         isMaximized: false,
1445                         isMinimized: false,
1446                         isCollapsed: false,
1447                         timestamp: $time()
1448                 });
1450                 if (options.type != 'window'){
1451                         options.container = document.body;
1452                         options.minimizable = false;
1453                 }
1454                 if (!options.container){
1455                         options.container = MUI.Desktop && MUI.Desktop.desktop ? MUI.Desktop.desktop : document.body;
1456                 }
1458                 // Set this.options.resizable to default if it was not defined
1459                 if (options.resizable == null){
1460                         if (options.type != 'window' || options.shape == 'gauge'){
1461                                 options.resizable = false;
1462                         }
1463                         else {
1464                                 options.resizable = true;
1465                         }
1466                 }
1468                 // Set this.options.draggable if it was not defined
1469                 if (options.draggable == null){
1470                         options.draggable = options.type != 'window' ? false : true;
1471                 }
1473                 // Gauges are not maximizable or resizable
1474                 if (options.shape == 'gauge' || options.type == 'notification'){
1475                         options.collapsible = false;
1476                         options.maximizable = false;
1477                         options.contentBgColor = 'transparent';
1478                         options.scrollbars = false;
1479                         options.footerHeight = 0;
1480                 }
1481                 if (options.type == 'notification'){
1482                         options.closable = false;
1483                         options.headerHeight = 0;
1484                 }
1486                 // Minimizable, dock is required and window cannot be modal
1487                 if (MUI.Dock && $(MUI.options.dock)){
1488                         if (MUI.Dock.dock && options.type != 'modal' && options.type != 'modal2'){
1489                                 options.minimizable = options.minimizable;
1490                         }
1491                 }
1492                 else {
1493                         options.minimizable = false;
1494                 }
1496                 // Maximizable, desktop is required
1497                 options.maximizable = MUI.Desktop && MUI.Desktop.desktop && options.maximizable && options.type != 'modal' && options.type != 'modal2';
1499                 if (this.options.type == 'modal2') {
1500                         this.options.shadowBlur = 0;
1501                         this.options.shadowOffset = {'x': 0, 'y': 0};
1502                         this.options.useSpinner = false;
1503                         this.options.useCanvas = false;
1504                         this.options.footerHeight = 0;
1505                         this.options.headerHeight = 0;
1506                 }
1508                 // If window has no ID, give it one.
1509                 options.id = options.id || 'win' + (++MUI.Windows.windowIDCount);
1511                 this.windowEl = $(options.id);
1513                 if (options.require.css.length || options.require.images.length){
1514                         new MUI.Require({
1515                                 css: options.require.css,
1516                                 images: options.require.images,
1517                                 onload: function(){
1518                                         this.newWindow();
1519                                 }.bind(this)
1520                         });
1521                 }
1522                 else {
1523                         this.newWindow();
1524                 }
1526                 // Return window object
1527                 return this;
1528         },
1529         saveValues: function(){
1530                 var coordinates = this.windowEl.getCoordinates();
1531                 this.options.x = coordinates.left.toInt();
1532                 this.options.y = coordinates.top.toInt();
1533         },
1535         /*
1537         Internal Function: newWindow
1539         Arguments:
1540                 properties
1542         */
1543         newWindow: function(properties){ // options is not doing anything
1545                 // Shorten object chain
1546                 var instances = MUI.Windows.instances;
1547                 var instanceID = MUI.Windows.instances.get(this.options.id);
1548                 var options = this.options;
1550                 // Here we check to see if there is already a class instance for this window
1551                 if (instanceID) var instance = instanceID;
1553                 // Check if window already exists and is not in progress of closing
1554                 if ( this.windowEl && !this.isClosing ){
1555                          // Restore if minimized
1556                         if (instance.isMinimized){
1557                                 MUI.Dock.restoreMinimized(this.windowEl);
1558                         }
1559                         // Expand and focus if collapsed
1560                         else if (instance.isCollapsed){
1561                                 MUI.collapseToggle(this.windowEl);
1562                                 setTimeout(MUI.focusWindow.pass(this.windowEl, this),10);
1563                         }
1564                         else if (this.windowEl.hasClass('windowClosed')){
1566                                 if (instance.check) instance.check.show();
1568                                 this.windowEl.removeClass('windowClosed');
1569                                 this.windowEl.setStyle('opacity', 0);
1570                                 this.windowEl.addClass('mocha');
1572                                 if (MUI.Dock && $(MUI.options.dock) && instance.options.type == 'window') {
1573                                         var currentButton = $(instance.options.id + '_dockTab');
1574                                         if (currentButton != null) {
1575                                                 currentButton.show();
1576                                         }
1577                                         MUI.Desktop.setDesktopSize();
1578                                 }
1580                                 instance.displayNewWindow();
1582                         }
1583                         // Else focus
1584                         else {
1585                                 var coordinates = document.getCoordinates();
1586                                 if (this.windowEl.getStyle('left').toInt() > coordinates.width || this.windowEl.getStyle('top').toInt() > coordinates.height){
1587                                         MUI.centerWindow(this.windowEl);
1588                                 }
1589                                 setTimeout(MUI.focusWindow.pass(this.windowEl, this),10);
1590                                 if (MUI.options.standardEffects == true) {
1591                                         this.windowEl.shake();
1592                                 }
1593                         }
1594                         return;
1595                 }
1596                 else {
1597                         instances.set(options.id, this);
1598                 }
1600                 this.isClosing = false;
1601                 this.fireEvent('onBeforeBuild');
1603                 // Create window div
1604                 MUI.Windows.indexLevel++;
1605                 this.windowEl = new Element('div', {
1606                         'class': 'mocha',
1607                         'id': options.id,
1608                         'styles': {
1609                                 'position': 'absolute',
1610                                 'width': options.width,
1611                                 'height': options.height,
1612                                 'display': 'block',
1613                                 'opacity': 0,
1614                                 'zIndex': MUI.Windows.indexLevel += 2
1615                         }
1616                 });
1618                 this.windowEl.store('instance', this);
1620                 this.windowEl.addClass(options.addClass);
1622                 if (options.type == 'modal2') {
1623                         this.windowEl.addClass('modal2');
1624                 }
1626                 // Fix a mouseover issue with gauges in IE7
1627                 if ( MUI.ieLegacySupport && options.shape == 'gauge') {
1628                         this.windowEl.setStyle('backgroundImage', 'url(../../images/spacer.gif)');
1629                 }
1631                 if (options.loadMethod == 'iframe') {
1632                         options.padding = { top: 0, right: 0, bottom: 0, left: 0 };
1633                 }
1635                 // Insert sub elements inside windowEl
1636                 this.insertWindowElements();
1638                 // Set title
1639                 this.titleEl.set('html', options.title);
1641                 this.contentWrapperEl.setStyle('overflow', 'hidden');
1643                 this.contentEl.setStyles({
1644                         'padding-top': options.padding.top,
1645                         'padding-bottom': options.padding.bottom,
1646                         'padding-left': options.padding.left,
1647                         'padding-right': options.padding.right
1648                 });
1650                 if (options.shape == 'gauge'){
1651                         if (options.useCanvasControls){
1652                                 this.canvasControlsEl.setStyle('visibility', 'hidden');
1653                         }
1654                         else {
1655                                 this.controlsEl.setStyle('visibility', 'hidden');
1656                         }
1657                         this.windowEl.addEvent('mouseover', function(){
1658                                 this.mouseover = true;
1659                                 var showControls = function(){
1660                                         if (this.mouseover != false){
1661                                                 if (options.useCanvasControls){
1662                                                         this.canvasControlsEl.setStyle('visibility', 'visible');
1663                                                 }
1664                                                 else {
1665                                                         this.controlsEl.setStyle('visibility', 'visible');
1666                                                 }
1667                                                 this.canvasHeaderEl.setStyle('visibility', 'visible');
1668                                                 this.titleEl.show();
1669                                         }
1670                                 };
1671                                 showControls.delay(0, this);
1673                         }.bind(this));
1674                         this.windowEl.addEvent('mouseleave', function(){
1675                                 this.mouseover = false;
1676                                 if (this.options.useCanvasControls){
1677                                         this.canvasControlsEl.setStyle('visibility', 'hidden');
1678                                 }
1679                                 else {
1680                                         this.controlsEl.setStyle('visibility', 'hidden');
1681                                 }
1682                                 this.canvasHeaderEl.setStyle('visibility', 'hidden');
1683                                 this.titleEl.hide();
1684                         }.bind(this));
1685                 }
1687                 // Inject window into DOM
1688                 this.windowEl.inject(options.container);
1690                 // Convert CSS colors to Canvas colors.
1691                 this.setColors();
1693                 if (options.type != 'notification'){
1694                         this.setMochaControlsWidth();
1695                 }
1697                 // Add content to window.
1698                 MUI.updateContent({
1699                         'element': this.windowEl,
1700                         'content': options.content,
1701                         'method': options.method,
1702                         'url': options.contentURL,
1703                         'data': options.data,
1704                         'onContentLoaded': null,
1705                         'require': {
1706                                 js: options.require.js,
1707                                 onload: options.require.onload
1708                         }
1709                 });
1711                 // Add content to window toolbar.
1712                 if (this.options.toolbar == true){
1713                         MUI.updateContent({
1714                                 'element': this.windowEl,
1715                                 'childElement': this.toolbarEl,
1716                                 'content': options.toolbarContent,
1717                                 'loadMethod': 'xhr',
1718                                 'method': options.method,
1719                                 'url': options.toolbarURL,
1720                                 'data': options.toolbarData,
1721                                 'onContentLoaded': options.toolbarOnload
1722                         });
1723                 }
1725                 // Add content to window toolbar.
1726                 if (this.options.toolbar2 == true){
1727                         MUI.updateContent({
1728                                 'element': this.windowEl,
1729                                 'childElement': this.toolbar2El,
1730                                 'content': options.toolbar2Content,
1731                                 'loadMethod': 'xhr',
1732                                 'method': options.method,
1733                                 'url': options.toolbar2URL,
1734                                 'data': options.toolbar2Data,
1735                                 'onContentLoaded': options.toolbar2Onload
1736                         });
1737                 }
1739                 this.drawWindow();
1741                 // Attach events to the window
1742                 this.attachDraggable();
1743                 this.attachResizable();
1744                 this.setupEvents();
1746                 if (options.resizable){
1747                         this.adjustHandles();
1748                 }
1750                 // Position window. If position not specified by user then center the window on the page.
1751                 if (options.container == document.body || options.container == MUI.Desktop.desktop){
1752                         var dimensions = window.getSize();
1753                 }
1754                 else {
1755                         var dimensions = $(this.options.container).getSize();
1756                 }
1758                 if (!options.y) {
1759                         if (MUI.Desktop && MUI.Desktop.desktop) {
1760                                 var y = (dimensions.y * .5) - (this.windowEl.offsetHeight * .5);
1761                                 if (y < -options.shadowBlur) y = -options.shadowBlur;
1762                         }
1763                         else {
1764                                 var y = window.getScroll().y + (window.getSize().y * .5) - (this.windowEl.offsetHeight * .5);
1765                                 if (y < -options.shadowBlur) y = -options.shadowBlur;
1766                         }
1767                 }
1768                 else {
1769                         var y = options.y - options.shadowBlur;
1770                 }
1772                 if (!this.options.x) {
1773                         var x = (dimensions.x * .5) - (this.windowEl.offsetWidth * .5);
1774                         if (x < -options.shadowBlur) x = -options.shadowBlur;
1775                 }
1776                 else {
1777                         var x = options.x - options.shadowBlur;
1778                 }
1780                 this.windowEl.setStyles({
1781                         'top': y,
1782                         'left': x
1783                 });
1785                 // Create opacityMorph
1787                 this.opacityMorph = new Fx.Morph(this.windowEl, {
1788                         'duration': 350,
1789                         transition: Fx.Transitions.Sine.easeInOut,
1790                         onComplete: function(){
1791                                 if (MUI.ieLegacySupport){
1792                                         this.drawWindow();
1793                                 }
1794                         }.bind(this)
1795                 });
1797                 this.displayNewWindow();
1799                 // This is a generic morph that can be reused later by functions like centerWindow()
1800                 // It returns the windowEl element rather than this Class.
1801                 this.morph = new Fx.Morph(this.windowEl, {
1802                         'duration': 200
1803                 });
1804                 this.windowEl.store('morph', this.morph);
1806                 this.resizeMorph = new Fx.Elements([this.contentWrapperEl, this.windowEl], {
1807                         duration: 400,
1808                         transition: Fx.Transitions.Sine.easeInOut,
1809                         onStart: function(){
1810                                 this.resizeAnimation = this.drawWindow.periodical(20, this);
1811                         }.bind(this),
1812                         onComplete: function(){
1813                                 $clear(this.resizeAnimation);
1814                                 this.drawWindow();
1815                                 // Show iframe
1816                                 if ( this.iframeEl ) {
1817                                         this.iframeEl.setStyle('visibility', 'visible');
1818                                 }
1819                         }.bind(this)
1820                 });
1821                 this.windowEl.store('resizeMorph', this.resizeMorph);
1823                 // Add check mark to menu if link exists in menu
1824                 // Need to make sure the check mark is not added to links not in menu
1825                 if ($(this.windowEl.id + 'LinkCheck')){
1826                         this.check = new Element('div', {
1827                                 'class': 'check',
1828                                 'id': this.options.id + '_check'
1829                         }).inject(this.windowEl.id + 'LinkCheck');
1830                 }
1832                 if (this.options.closeAfter != false){
1833                         MUI.closeWindow.delay(this.options.closeAfter, this, this.windowEl);
1834                 }
1836                 if (MUI.Dock && $(MUI.options.dock) && this.options.type == 'window' ){
1837                         MUI.Dock.createDockTab(this.windowEl);
1838                 }
1840         },
1841         displayNewWindow: function(){
1843                 options = this.options;
1844                 if (options.type == 'modal' || options.type == 'modal2') {
1845                         MUI.currentModal = this.windowEl;
1846                         if (Browser.Engine.trident4){
1847                                 $('modalFix').show();
1848                         }
1849                         $('modalOverlay').show();
1850                         if (MUI.options.advancedEffects == false){
1851                                 $('modalOverlay').setStyle('opacity', .6);
1852                                 this.windowEl.setStyles({
1853                                         'zIndex': 11000,
1854                                         'opacity': 1
1855                                 });
1856                         }
1857                         else {
1858                                 MUI.Modal.modalOverlayCloseMorph.cancel();
1859                                 MUI.Modal.modalOverlayOpenMorph.start({
1860                                         'opacity': .6
1861                                 });
1862                                 this.windowEl.setStyles({
1863                                         'zIndex': 11000
1864                                 });
1865                                 this.opacityMorph.start({
1866                                         'opacity': 1
1867                                 });
1868                         }
1870                         $$('.dockTab').removeClass('activeDockTab');
1871                         $$('.mocha').removeClass('isFocused');
1872                         this.windowEl.addClass('isFocused');
1874                 }
1875                 else if (MUI.options.advancedEffects == false){
1876                         this.windowEl.setStyle('opacity', 1);
1877                         setTimeout(MUI.focusWindow.pass(this.windowEl, this), 10);
1878                 }
1879                 else {
1880                         // IE cannot handle both element opacity and VML alpha at the same time.
1881                         if (MUI.ieLegacySupport){
1882                                 this.drawWindow(false);
1883                         }
1884                         this.opacityMorph.start({
1885                                 'opacity': 1
1886                         });
1887                         setTimeout(MUI.focusWindow.pass(this.windowEl, this), 10);
1888                 }
1890         },
1891         setupEvents: function() {
1892                 var windowEl = this.windowEl;
1893                 // Set events
1894                 // Note: if a button does not exist, its due to properties passed to newWindow() stating otherwise
1895                 if (this.closeButtonEl){
1896                         this.closeButtonEl.addEvent('click', function(e) {
1897                                 new Event(e).stop();
1898                                 MUI.closeWindow(windowEl);
1899                         }.bind(this));
1900                 }
1902                 if (this.options.type == 'window'){
1903                         windowEl.addEvent('mousedown', function(e) {
1904                                 if (MUI.ieLegacySupport) {
1905                                         new Event(e).stop();
1906                                 }
1907                                 MUI.focusWindow(windowEl);
1908                                 if (windowEl.getStyle('top').toInt() < -this.options.shadowBlur) {
1909                                         windowEl.setStyle('top', -this.options.shadowBlur);
1910                                 }
1911                         }.bind(this));
1912                 }
1914                 if (this.minimizeButtonEl) {
1915                         this.minimizeButtonEl.addEvent('click', function(e) {
1916                                 new Event(e).stop();
1917                                 MUI.Dock.minimizeWindow(windowEl);
1918                 }.bind(this));
1919                 }
1921                 if (this.maximizeButtonEl) {
1922                         this.maximizeButtonEl.addEvent('click', function(e) {
1923                                 new Event(e).stop();
1924                                 if (this.isMaximized) {
1925                                         MUI.Desktop.restoreWindow(windowEl);
1926                                 } else {
1927                                         MUI.Desktop.maximizeWindow(windowEl);
1928                                 }
1929                         }.bind(this));
1930                 }
1932                 if (this.options.collapsible == true){
1933                         // Keep titlebar text from being selected on double click in Safari.
1934                         this.titleEl.addEvent('selectstart', function(e) {
1935                                 e = new Event(e).stop();
1936                         }.bind(this));
1938                         if (MUI.ieLegacySupport) {
1939                                 this.titleBarEl.addEvent('mousedown', function(e) {
1940                                         this.titleEl.setCapture();
1941                                 }.bind(this));
1942                                 this.titleBarEl.addEvent('mouseup', function(e) {
1943                                                 this.titleEl.releaseCapture();
1944                                 }.bind(this));
1945                         }
1947                         this.titleBarEl.addEvent('dblclick', function(e) {
1948                                 e = new Event(e).stop();
1949                                 MUI.collapseToggle(this.windowEl);
1950                         }.bind(this));
1951                 }
1953         },
1954         /*
1956         Internal Function: attachDraggable()
1957                 Make window draggable.
1959         */
1960         attachDraggable: function(){
1961                 var windowEl = this.windowEl;
1962                 if (!this.options.draggable) return;
1963                 this.windowDrag = new Drag.Move(windowEl, {
1964                         handle: this.titleBarEl,
1965                         container: this.options.restrict == true ? $(this.options.container) : false,
1966                         grid: this.options.draggableGrid,
1967                         limit: this.options.draggableLimit,
1968                         snap: this.options.draggableSnap,
1969                         onStart: function() {
1970                                 if (this.options.type != 'modal' && this.options.type != 'modal2'){
1971                                         MUI.focusWindow(windowEl);
1972                                         $('windowUnderlay').show();
1973                                 }
1974                                 if (this.iframeEl) {
1975                                         if (!MUI.ieLegacySupport) {
1976                                                 this.iframeEl.setStyle('visibility', 'hidden');
1977                                         }
1978                                         else {
1979                                                 this.iframeEl.hide();
1980                                         }
1981                                 }
1982                         }.bind(this),
1983                         onComplete: function() {
1984                                 if (this.options.type != 'modal' && this.options.type != 'modal2') {
1985                                         $('windowUnderlay').hide();
1986                                 }
1987                                 if ( this.iframeEl ){
1988                                         if (!MUI.ieLegacySupport) {
1989                                                 this.iframeEl.setStyle('visibility', 'visible');
1990                                         }
1991                                         else {
1992                                                 this.iframeEl.show();
1993                                         }
1994                                 }
1995                                 // Store new position in options.
1996                                 this.saveValues();
1997                         }.bind(this)
1998                 });
1999         },
2000         /*
2002         Internal Function: attachResizable
2003                 Make window resizable.
2005         */
2006         attachResizable: function(){
2007                 var windowEl = this.windowEl;
2008                 if (!this.options.resizable) return;
2009                 this.resizable1 = this.windowEl.makeResizable({
2010                         handle: [this.n, this.ne, this.nw],
2011                         limit: {
2012                                 y: [
2013                                         function(){
2014                                                 return this.windowEl.getStyle('top').toInt() + this.windowEl.getStyle('height').toInt() - this.options.resizeLimit.y[1];
2015                                         }.bind(this),
2016                                         function(){
2017                                                 return this.windowEl.getStyle('top').toInt() + this.windowEl.getStyle('height').toInt() - this.options.resizeLimit.y[0];
2018                                         }.bind(this)
2019                                 ]
2020                         },
2021                         modifiers: {x: false, y: 'top'},
2022                         onStart: function(){
2023                                 this.resizeOnStart();
2024                                 this.coords = this.contentWrapperEl.getCoordinates();
2025                                 this.y2 = this.coords.top.toInt() + this.contentWrapperEl.offsetHeight;
2026                         }.bind(this),
2027                         onDrag: function(){
2028                                 this.coords = this.contentWrapperEl.getCoordinates();
2029                                 this.contentWrapperEl.setStyle('height', this.y2 - this.coords.top.toInt());
2030                                 this.resizeOnDrag();
2031                         }.bind(this),
2032                         onComplete: function(){
2033                                 this.resizeOnComplete();
2034                         }.bind(this)
2035                 });
2037                 this.resizable2 = this.contentWrapperEl.makeResizable({
2038                         handle: [this.e, this.ne],
2039                         limit: {
2040                                 x: [this.options.resizeLimit.x[0] - (this.options.shadowBlur * 2), this.options.resizeLimit.x[1] - (this.options.shadowBlur * 2) ]
2041                         },
2042                         modifiers: {x: 'width', y: false},
2043                         onStart: function(){
2044                                 this.resizeOnStart();
2045                         }.bind(this),
2046                         onDrag: function(){
2047                                 this.resizeOnDrag();
2048                         }.bind(this),
2049                         onComplete: function(){
2050                                 this.resizeOnComplete();
2051                         }.bind(this)
2052                 });
2054                 this.resizable3 = this.contentWrapperEl.makeResizable({
2055                         container: this.options.restrict == true ? $(this.options.container) : false,
2056                         handle: this.se,
2057                         limit: {
2058                                 x: [this.options.resizeLimit.x[0] - (this.options.shadowBlur * 2), this.options.resizeLimit.x[1] - (this.options.shadowBlur * 2) ],
2059                                 y: [this.options.resizeLimit.y[0] - this.headerFooterShadow, this.options.resizeLimit.y[1] - this.headerFooterShadow]
2060                         },
2061                         modifiers: {x: 'width', y: 'height'},
2062                         onStart: function(){
2063                                 this.resizeOnStart();
2064                         }.bind(this),
2065                         onDrag: function(){
2066                                 this.resizeOnDrag();
2067                         }.bind(this),
2068                         onComplete: function(){
2069                                 this.resizeOnComplete();
2070                         }.bind(this)
2071                 });
2073                 this.resizable4 = this.contentWrapperEl.makeResizable({
2074                         handle: [this.s, this.sw],
2075                         limit: {
2076                                 y: [this.options.resizeLimit.y[0] - this.headerFooterShadow, this.options.resizeLimit.y[1] - this.headerFooterShadow]
2077                         },
2078                         modifiers: {x: false, y: 'height'},
2079                         onStart: function(){
2080                                 this.resizeOnStart();
2081                         }.bind(this),
2082                         onDrag: function(){
2083                                 this.resizeOnDrag();
2084                         }.bind(this),
2085                         onComplete: function(){
2086                                 this.resizeOnComplete();
2087                         }.bind(this)
2088                 });
2090                 this.resizable5 = this.windowEl.makeResizable({
2091                         handle: [this.w, this.sw, this.nw],
2092                         limit: {
2093                                 x: [
2094                                         function(){
2095                                                 return this.windowEl.getStyle('left').toInt() + this.windowEl.getStyle('width').toInt() - this.options.resizeLimit.x[1];
2096                                         }.bind(this),
2097                                    function(){
2098                                            return this.windowEl.getStyle('left').toInt() + this.windowEl.getStyle('width').toInt() - this.options.resizeLimit.x[0];
2099                                         }.bind(this)
2100                                 ]
2101                         },
2102                         modifiers: {x: 'left', y: false},
2103                         onStart: function(){
2104                                 this.resizeOnStart();
2105                                 this.coords = this.contentWrapperEl.getCoordinates();
2106                                 this.x2 = this.coords.left.toInt() + this.contentWrapperEl.offsetWidth;
2107                         }.bind(this),
2108                         onDrag: function(){
2109                                 this.coords = this.contentWrapperEl.getCoordinates();
2110                                 this.contentWrapperEl.setStyle('width', this.x2 - this.coords.left.toInt());
2111                                 this.resizeOnDrag();
2112                         }.bind(this),
2113                         onComplete: function(){
2114                                 this.resizeOnComplete();
2115                         }.bind(this)
2116                 });
2118         },
2119         resizeOnStart: function(){
2120                 $('windowUnderlay').show();
2121                 if (this.iframeEl){
2122                         if (!MUI.ieLegacySupport) {
2123                                 this.iframeEl.setStyle('visibility', 'hidden');
2124                         }
2125                         else {
2126                                 this.iframeEl.hide();
2127                         }
2128                 }
2129         },
2130         resizeOnDrag: function(){
2131                 this.drawWindow();
2132                 this.adjustHandles();
2133         },
2134         resizeOnComplete: function(){
2135                 $('windowUnderlay').hide();
2136                 if (this.iframeEl){
2137                         if (!MUI.ieLegacySupport) {
2138                                 this.iframeEl.setStyle('visibility', 'visible');
2139                         }
2140                         else {
2141                                 this.iframeEl.show();
2142                                 // The following hack is to get IE8 RC1 IE8 Standards Mode to properly resize an iframe
2143                                 // when only the vertical dimension is changed.
2144                                 this.iframeEl.setStyle('width', '99%');
2145                                 this.iframeEl.setStyle('height', this.contentWrapperEl.offsetHeight);
2146                                 this.iframeEl.setStyle('width', '100%');
2147                                 this.iframeEl.setStyle('height', this.contentWrapperEl.offsetHeight);
2148                         }
2149                 }
2151                 // Resize panels if there are any
2152                 if (this.contentWrapperEl.getChildren('.column') != null) {
2153                         MUI.rWidth(this.contentWrapperEl);
2154                         this.contentWrapperEl.getChildren('.column').each(function(column){
2155                                 MUI.panelHeight(column);
2156                         });
2157                 }
2159                 this.fireEvent('onResize', this.windowEl);
2160         },
2161         adjustHandles: function(){
2163                 var shadowBlur = this.options.shadowBlur;
2164                 var shadowBlur2x = shadowBlur * 2;
2165                 var shadowOffset = this.options.shadowOffset;
2166                 var top = shadowBlur - shadowOffset.y - 1;
2167                 var right = shadowBlur + shadowOffset.x - 1;
2168                 var bottom = shadowBlur + shadowOffset.y - 1;
2169                 var left = shadowBlur - shadowOffset.x - 1;
2171                 var coordinates = this.windowEl.getCoordinates();
2172                 var width = coordinates.width - shadowBlur2x + 2;
2173                 var height = coordinates.height - shadowBlur2x + 2;
2175                 this.n.setStyles({
2176                         'top': top,
2177                         'left': left + 10,
2178                         'width': width - 20
2179                 });
2180                 this.e.setStyles({
2181                         'top': top + 10,
2182                         'right': right,
2183                         'height': height - 30
2184                 });
2185                 this.s.setStyles({
2186                         'bottom': bottom,
2187                         'left': left + 10,
2188                         'width': width - 30
2189                 });
2190                 this.w.setStyles({
2191                         'top': top + 10,
2192                         'left': left,
2193                         'height': height - 20
2194                 });
2195                 this.ne.setStyles({
2196                         'top': top,
2197                         'right': right
2198                 });
2199                 this.se.setStyles({
2200                         'bottom': bottom,
2201                         'right': right
2202                 });
2203                 this.sw.setStyles({
2204                         'bottom': bottom,
2205                         'left': left
2206                 });
2207                 this.nw.setStyles({
2208                         'top': top,
2209                         'left': left
2210                 });
2211         },
2212         detachResizable: function(){
2213                         this.resizable1.detach();
2214                         this.resizable2.detach();
2215                         this.resizable3.detach();
2216                         this.resizable4.detach();
2217                         this.resizable5.detach();
2218                         this.windowEl.getElements('.handle').hide();
2219         },
2220         reattachResizable: function(){
2221                         this.resizable1.attach();
2222                         this.resizable2.attach();
2223                         this.resizable3.attach();
2224                         this.resizable4.attach();
2225                         this.resizable5.attach();
2226                         this.windowEl.getElements('.handle').show();
2227         },
2228         /*
2230         Internal Function: insertWindowElements
2232         Arguments:
2233                 windowEl
2235         */
2236         insertWindowElements: function(){
2238                 var options = this.options;
2239                 var height = options.height;
2240                 var width = options.width;
2241                 var id = options.id;
2243                 var cache = {};
2245                 if (Browser.Engine.trident4){
2246                         cache.zIndexFixEl = new Element('iframe', {
2247                                 'id': id + '_zIndexFix',
2248                                 'class': 'zIndexFix',
2249                                 'scrolling': 'no',
2250                                 'marginWidth': 0,
2251                                 'marginHeight': 0,
2252                                 'src': '',
2253                                 'styles': {
2254                                         'position': 'absolute' // This is set here to make theme transitions smoother
2255                                 }
2256                         }).inject(this.windowEl);
2257                 }
2259                 cache.overlayEl = new Element('div', {
2260                         'id': id + '_overlay',
2261                         'class': 'mochaOverlay',
2262                         'styles': {
2263                                 'position': 'absolute', // This is set here to make theme transitions smoother
2264                                 'top': 0,
2265                                 'left': 0
2266                         }
2267                 }).inject(this.windowEl);
2269                 cache.titleBarEl = new Element('div', {
2270                         'id': id + '_titleBar',
2271                         'class': 'mochaTitlebar',
2272                         'styles': {
2273                                 'cursor': options.draggable ? 'move' : 'default'
2274                         }
2275                 }).inject(cache.overlayEl, 'top');
2277                 cache.titleEl = new Element('h3', {
2278                         'id': id + '_title',
2279                         'class': 'mochaTitle'
2280                 }).inject(cache.titleBarEl);
2282                 if (options.icon != false){
2283                         cache.titleEl.setStyles({
2284                                 'padding-left': 28,
2285                                 'background': 'url(' + options.icon + ') 5px 4px no-repeat'
2286                         });
2287                 }
2289                 cache.contentBorderEl = new Element('div', {
2290                         'id': id + '_contentBorder',
2291                         'class': 'mochaContentBorder'
2292                 }).inject(cache.overlayEl);
2294                 if (options.toolbar){
2295                         cache.toolbarWrapperEl = new Element('div', {
2296                                 'id': id + '_toolbarWrapper',
2297                                 'class': 'mochaToolbarWrapper',
2298                                 'styles': { 'height': options.toolbarHeight }
2299                         }).inject(cache.contentBorderEl, options.toolbarPosition == 'bottom' ? 'after' : 'before');
2301                         if (options.toolbarPosition == 'bottom') {
2302                                 cache.toolbarWrapperEl.addClass('bottom');
2303                         }
2304                         cache.toolbarEl = new Element('div', {
2305                                 'id': id + '_toolbar',
2306                                 'class': 'mochaToolbar',
2307                                 'styles': { 'height': options.toolbarHeight }
2308                         }).inject(cache.toolbarWrapperEl);
2309                 }
2311                 if (options.toolbar2){
2312                         cache.toolbar2WrapperEl = new Element('div', {
2313                                 'id': id + '_toolbar2Wrapper',
2314                                 'class': 'mochaToolbarWrapper',
2315                                 'styles': { 'height': options.toolbar2Height }
2316                         }).inject(cache.contentBorderEl, options.toolbar2Position == 'bottom' ? 'after' : 'before');
2318                         if (options.toolbar2Position == 'bottom') {
2319                                 cache.toolbar2WrapperEl.addClass('bottom');
2320                         }
2321                         cache.toolbar2El = new Element('div', {
2322                                 'id': id + '_toolbar2',
2323                                 'class': 'mochaToolbar',
2324                                 'styles': { 'height': options.toolbar2Height }
2325                         }).inject(cache.toolbar2WrapperEl);
2326                 }
2328                 cache.contentWrapperEl = new Element('div', {
2329                         'id': id + '_contentWrapper',
2330                         'class': 'mochaContentWrapper',
2331                         'styles': {
2332                                 'width': width + 'px',
2333                                 'height': height + 'px'
2334                         }
2335                 }).inject(cache.contentBorderEl);
2337                 if (this.options.shape == 'gauge'){
2338                         cache.contentBorderEl.setStyle('borderWidth', 0);
2339                 }
2341                 cache.contentEl = new Element('div', {
2342                         'id': id + '_content',
2343                         'class': 'mochaContent'
2344                 }).inject(cache.contentWrapperEl);
2346                 if (this.options.useCanvas == true && !MUI.ieLegacySupport) {
2347                         cache.canvasEl = new Element('canvas', {
2348                                 'id': id + '_canvas',
2349                                 'class': 'mochaCanvas',
2350                                 'width': 10,
2351                                 'height': 10
2352                         }).inject(this.windowEl);
2353                 }
2355                 if (this.options.useCanvas == true && MUI.ieLegacySupport) {
2356                         cache.canvasEl = new Element('canvas', {
2357                                 'id': id + '_canvas',
2358                                 'class': 'mochaCanvas',
2359                                 'width': 50000, // IE8 excanvas requires these large numbers
2360                                 'height': 20000,
2361                                 'styles': {
2362                                         'position': 'absolute',
2363                                         'top': 0,
2364                                         'left': 0
2365                                 }
2366                         }).inject(this.windowEl);
2368                         if (MUI.ieLegacySupport && MUI.ieSupport == 'excanvas'){
2369                                 G_vmlCanvasManager.initElement(cache.canvasEl);
2370                                 cache.canvasEl = this.windowEl.getElement('.mochaCanvas');
2371                         }
2372                 }
2374                 cache.controlsEl = new Element('div', {
2375                         'id': id + '_controls',
2376                         'class': 'mochaControls'
2377                 }).inject(cache.overlayEl, 'after');
2379                 if (options.useCanvasControls == true){
2380                         cache.canvasControlsEl = new Element('canvas', {
2381                                 'id': id + '_canvasControls',
2382                                 'class': 'mochaCanvasControls',
2383                                 'width': 14,
2384                                 'height': 14
2385                         }).inject(this.windowEl);
2387                         if (MUI.ieLegacySupport && MUI.ieSupport == 'excanvas'){
2388                                 G_vmlCanvasManager.initElement(cache.canvasControlsEl);
2389                                 cache.canvasControlsEl = this.windowEl.getElement('.mochaCanvasControls');
2390                         }
2391                 }
2393                 if (options.closable){
2394                         cache.closeButtonEl = new Element('div', {
2395                                 'id': id + '_closeButton',
2396                                 'class': 'mochaCloseButton mochaWindowButton',
2397                                 'title': 'Close'
2398                         }).inject(cache.controlsEl);
2399                 }
2401                 if (options.maximizable){
2402                         cache.maximizeButtonEl = new Element('div', {
2403                                 'id': id + '_maximizeButton',
2404                                 'class': 'mochaMaximizeButton mochaWindowButton',
2405                                 'title': 'Maximize'
2406                         }).inject(cache.controlsEl);
2407                 }
2409                 if (options.minimizable){
2410                         cache.minimizeButtonEl = new Element('div', {
2411                                 'id': id + '_minimizeButton',
2412                                 'class': 'mochaMinimizeButton mochaWindowButton',
2413                                 'title': 'Minimize'
2414                         }).inject(cache.controlsEl);
2415                 }
2417                 if (options.useSpinner == true && options.shape != 'gauge' && options.type != 'notification'){
2418                         cache.spinnerEl = new Element('div', {
2419                                 'id': id + '_spinner',
2420                                 'class': 'mochaSpinner',
2421                                 'width': 16,
2422                                 'height': 16
2423                         }).inject(this.windowEl, 'bottom');
2424                 }
2426                 if (this.options.shape == 'gauge'){
2427                         cache.canvasHeaderEl = new Element('canvas', {
2428                                 'id': id + '_canvasHeader',
2429                                 'class': 'mochaCanvasHeader',
2430                                 'width': this.options.width,
2431                                 'height': 26
2432                         }).inject(this.windowEl, 'bottom');
2434                         if (MUI.ieLegacySupport && MUI.ieSupport == 'excanvas'){
2435                                 G_vmlCanvasManager.initElement(cache.canvasHeaderEl);
2436                                 cache.canvasHeaderEl = this.windowEl.getElement('.mochaCanvasHeader');
2437                         }
2438                 }
2440                 if ( MUI.ieLegacySupport ){
2441                         cache.overlayEl.setStyle('zIndex', 2);
2442                 }
2444                 if (options.resizable){
2445                         cache.n = new Element('div', {
2446                                 'id': id + '_resizeHandle_n',
2447                                 'class': 'handle',
2448                                 'styles': {
2449                                         'top': 0,
2450                                         'left': 10,
2451                                         'cursor': 'n-resize'
2452                                 }
2453                         }).inject(cache.overlayEl, 'after');
2455                         cache.ne = new Element('div', {
2456                                 'id': id + '_resizeHandle_ne',
2457                                 'class': 'handle corner',
2458                                 'styles': {
2459                                         'top': 0,
2460                                         'right': 0,
2461                                         'cursor': 'ne-resize'
2462                                 }
2463                         }).inject(cache.overlayEl, 'after');
2465                         cache.e = new Element('div', {
2466                                 'id': id + '_resizeHandle_e',
2467                                 'class': 'handle',
2468                                 'styles': {
2469                                         'top': 10,
2470                                         'right': 0,
2471                                         'cursor': 'e-resize'
2472                                 }
2473                         }).inject(cache.overlayEl, 'after');
2475                         cache.se = new Element('div', {
2476                                 'id': id + '_resizeHandle_se',
2477                                 'class': 'handle cornerSE',
2478                                 'styles': {
2479                                         'bottom': 0,
2480                                         'right': 0,
2481                                         'cursor': 'se-resize'
2482                                 }
2483                         }).inject(cache.overlayEl, 'after');
2485                         cache.s = new Element('div', {
2486                                 'id': id + '_resizeHandle_s',
2487                                 'class': 'handle',
2488                                 'styles': {
2489                                         'bottom': 0,
2490                                         'left': 10,
2491                                         'cursor': 's-resize'
2492                                 }
2493                         }).inject(cache.overlayEl, 'after');
2495                         cache.sw = new Element('div', {
2496                                 'id': id + '_resizeHandle_sw',
2497                                 'class': 'handle corner',
2498                                 'styles': {
2499                                         'bottom': 0,
2500                                         'left': 0,
2501                                         'cursor': 'sw-resize'
2502                                 }
2503                         }).inject(cache.overlayEl, 'after');
2505                         cache.w = new Element('div', {
2506                                 'id': id + '_resizeHandle_w',
2507                                 'class': 'handle',
2508                                 'styles': {
2509                                         'top': 10,
2510                                         'left': 0,
2511                                         'cursor': 'w-resize'
2512                                 }
2513                         }).inject(cache.overlayEl, 'after');
2515                         cache.nw = new Element('div', {
2516                                 'id': id + '_resizeHandle_nw',
2517                                 'class': 'handle corner',
2518                                 'styles': {
2519                                         'top': 0,
2520                                         'left': 0,
2521                                         'cursor': 'nw-resize'
2522                                 }
2523                         }).inject(cache.overlayEl, 'after');
2524                 }
2525                 $extend(this, cache);
2527         },
2528         /*
2530         Convert CSS colors to Canvas colors.
2532         */
2533         setColors: function(){
2535                 if (this.options.useCanvas == true) {
2537                         // Set TitlebarColor
2538                         var pattern = /\?(.*?)\)/;
2539                         if (this.titleBarEl.getStyle('backgroundImage') != 'none'){
2540                                 var gradient = this.titleBarEl.getStyle('backgroundImage');
2541                                 gradient = gradient.match(pattern)[1];
2542                                 gradient = gradient.parseQueryString();
2543                                 var gradientFrom = gradient.from;
2544                                 var gradientTo = gradient.to.replace(/\"/, ''); // IE7 was adding a quotation mark in. No idea why.
2546                                 this.options.headerStartColor = new Color(gradientFrom);
2547                                 this.options.headerStopColor = new Color(gradientTo);
2548                                 this.titleBarEl.addClass('replaced');
2549                         }
2550                         else if (this.titleBarEl.getStyle('background-color') !== '' && this.titleBarEl.getStyle('background-color') !== 'transparent') {
2551                                 this.options.headerStartColor = new Color(this.titleBarEl.getStyle('background-color')).mix('#fff', 20);
2552                                 this.options.headerStopColor = new Color(this.titleBarEl.getStyle('background-color')).mix('#000', 20);
2553                                 this.titleBarEl.addClass('replaced');
2554                         }
2556                         // Set BodyBGColor
2557                         if (this.windowEl.getStyle('background-color') !== '' && this.windowEl.getStyle('background-color') !== 'transparent') {
2558                                 this.options.bodyBgColor = new Color(this.windowEl.getStyle('background-color'));
2559                                 this.windowEl.addClass('replaced');
2560                         }
2562                         // Set resizableColor, the color of the SE corner resize handle
2563                         if (this.options.resizable && this.se.getStyle('background-color') !== '' && this.se.getStyle('background-color') !== 'transparent') {
2564                                 this.options.resizableColor = new Color(this.se.getStyle('background-color'));
2565                                 this.se.addClass('replaced');
2566                         }
2568                 }
2570                 if (this.options.useCanvasControls == true){
2572                         if (this.minimizeButtonEl){
2574                                 // Set Minimize Button Foreground Color
2575                                 if (this.minimizeButtonEl.getStyle('color') !== '' && this.minimizeButtonEl.getStyle('color') !== 'transparent') {
2576                                         this.options.minimizeColor = new Color(this.minimizeButtonEl.getStyle('color'));
2577                                 }
2579                                 // Set Minimize Button Background Color
2580                                 if (this.minimizeButtonEl.getStyle('background-color') !== '' && this.minimizeButtonEl.getStyle('background-color') !== 'transparent') {
2581                                         this.options.minimizeBgColor = new Color(this.minimizeButtonEl.getStyle('background-color'));
2582                                         this.minimizeButtonEl.addClass('replaced');
2583                                 }
2585                         }
2587                         if (this.maximizeButtonEl){
2589                                 // Set Maximize Button Foreground Color
2590                                 if (this.maximizeButtonEl.getStyle('color') !== '' && this.maximizeButtonEl.getStyle('color') !== 'transparent') {
2591                                         this.options.maximizeColor = new Color(this.maximizeButtonEl.getStyle('color'));
2592                                 }
2594                                 // Set Maximize Button Background Color
2595                                 if (this.maximizeButtonEl.getStyle('background-color') !== '' && this.maximizeButtonEl.getStyle('background-color') !== 'transparent') {
2596                                         this.options.maximizeBgColor = new Color(this.maximizeButtonEl.getStyle('background-color'));
2597                                         this.maximizeButtonEl.addClass('replaced');
2598                                 }
2600                         }
2602                         if (this.closeButtonEl){
2604                                 // Set Close Button Foreground Color
2605                                 if (this.closeButtonEl.getStyle('color') !== '' && this.closeButtonEl.getStyle('color') !== 'transparent') {
2606                                         this.options.closeColor = new Color(this.closeButtonEl.getStyle('color'));
2607                                 }
2609                                 // Set Close Button Background Color
2610                                 if (this.closeButtonEl.getStyle('background-color') !== '' && this.closeButtonEl.getStyle('background-color') !== 'transparent') {
2611                                         this.options.closeBgColor = new Color(this.closeButtonEl.getStyle('background-color'));
2612                                         this.closeButtonEl.addClass('replaced');
2613                                 }
2615                         }
2616                 }
2617         },
2618         /*
2620         Internal function: drawWindow
2621                 This is where we create the canvas GUI
2623         Arguments:
2624                 windowEl: the $(window)
2625                 shadows: (boolean) false will draw a window without shadows
2627         */
2628         drawWindow: function(shadows) {
2630                 if (this.drawingWindow == true) return;
2631                 this.drawingWindow = true;
2633                 if (this.isCollapsed){
2634                         this.drawWindowCollapsed(shadows);
2635                         return;
2636                 }
2638                 var windowEl = this.windowEl;
2640                 var options = this.options;
2641                 var shadowBlur = options.shadowBlur;
2642                 var shadowBlur2x = shadowBlur * 2;
2643                 var shadowOffset = this.options.shadowOffset;
2645                 this.overlayEl.setStyles({
2646                         'width': this.contentWrapperEl.offsetWidth
2647                 });
2649                 // Resize iframe when window is resized
2650                 if (this.iframeEl) {
2651                         this.iframeEl.setStyle('height', this.contentWrapperEl.offsetHeight);
2652                 }
2654                 var borderHeight = this.contentBorderEl.getStyle('margin-top').toInt() + this.contentBorderEl.getStyle('margin-bottom').toInt();
2655                 var toolbarHeight = this.toolbarWrapperEl ? this.toolbarWrapperEl.getStyle('height').toInt() + this.toolbarWrapperEl.getStyle('margin-top').toInt() : 0;
2656                 var toolbar2Height = this.toolbar2WrapperEl ? this.toolbar2WrapperEl.getStyle('height').toInt() + this.toolbar2WrapperEl.getStyle('margin-top').toInt() : 0;
2658                 this.headerFooterShadow = options.headerHeight + options.footerHeight + shadowBlur2x;
2659                 var height = this.contentWrapperEl.getStyle('height').toInt() + this.headerFooterShadow + toolbarHeight + toolbar2Height + borderHeight;
2660                 var width = this.contentWrapperEl.getStyle('width').toInt() + shadowBlur2x;
2661                 this.windowEl.setStyles({
2662                         'height': height,
2663                         'width': width
2664                 });
2666                 this.overlayEl.setStyles({
2667                         'height': height,
2668                         'top': shadowBlur - shadowOffset.y,
2669                         'left': shadowBlur - shadowOffset.x
2670                 });
2672                 if (this.options.useCanvas == true) {
2673                         if (MUI.ieLegacySupport) {
2674                                 this.canvasEl.height = 20000;
2675                                 this.canvasEl.width = 50000;
2676                         }
2677                         this.canvasEl.height = height;
2678                         this.canvasEl.width = width;
2679                 }
2681                 // Part of the fix for IE6 select z-index bug
2682                 if (Browser.Engine.trident4){
2683                         this.zIndexFixEl.setStyles({
2684                                 'width': width,
2685                                 'height': height
2686                         })
2687                 }
2689                 this.titleBarEl.setStyles({
2690                         'width': width - shadowBlur2x,
2691                         'height': options.headerHeight
2692                 });
2694                 // Make sure loading icon is placed correctly.
2695                 if (options.useSpinner == true && options.shape != 'gauge' && options.type != 'notification'){
2696                         this.spinnerEl.setStyles({
2697                                 'left': shadowBlur - shadowOffset.x + 3,
2698                                 'bottom': shadowBlur + shadowOffset.y +  4
2699                         });
2700                 }
2702                 if (this.options.useCanvas != false) {
2704                         // Draw Window
2705                         var ctx = this.canvasEl.getContext('2d');
2706                         ctx.clearRect(0, 0, width, height);
2708                         switch (options.shape) {
2709                                 case 'box':
2710                                         this.drawBox(ctx, width, height, shadowBlur, shadowOffset, shadows);
2711                                         break;
2712                                 case 'gauge':
2713                                         this.drawGauge(ctx, width, height, shadowBlur, shadowOffset, shadows);
2714                                         break;
2715                         }
2717                         if (options.resizable){
2718                                 MUI.triangle(
2719                                         ctx,
2720                                         width - (shadowBlur + shadowOffset.x + 17),
2721                                         height - (shadowBlur + shadowOffset.y + 18),
2722                                         11,
2723                                         11,
2724                                         options.resizableColor,
2725                                         1.0
2726                                 );
2727                         }
2729                         // Invisible dummy object. The last element drawn is not rendered consistently while resizing in IE6 and IE7
2730                         if (MUI.ieLegacySupport){
2731                                 MUI.triangle(ctx, 0, 0, 10, 10, options.resizableColor, 0);
2732                         }
2733                 }
2735                 if (options.type != 'notification' && options.useCanvasControls == true){
2736                         this.drawControls(width, height, shadows);
2737                 }
2739                 // Resize panels if there are any
2740                 if (MUI.Desktop && this.contentWrapperEl.getChildren('.column').length != 0) {
2741                         MUI.rWidth(this.contentWrapperEl);
2742                         this.contentWrapperEl.getChildren('.column').each(function(column){
2743                                 MUI.panelHeight(column);
2744                         });
2745                 }
2747                 this.drawingWindow = false;
2748                 return this;
2750         },
2751         drawWindowCollapsed: function(shadows) {
2753                 var windowEl = this.windowEl;
2755                 var options = this.options;
2756                 var shadowBlur = options.shadowBlur;
2757                 var shadowBlur2x = shadowBlur * 2;
2758                 var shadowOffset = options.shadowOffset;
2760                 var headerShadow = options.headerHeight + shadowBlur2x + 2;
2761                 var height = headerShadow;
2762                 var width = this.contentWrapperEl.getStyle('width').toInt() + shadowBlur2x;
2763                 this.windowEl.setStyle('height', height);
2765                 this.overlayEl.setStyles({
2766                         'height': height,
2767                         'top': shadowBlur - shadowOffset.y,
2768                         'left': shadowBlur - shadowOffset.x
2769                 });
2771                 // Part of the fix for IE6 select z-index bug
2772                 if (Browser.Engine.trident4){
2773                         this.zIndexFixEl.setStyles({
2774                                 'width': width,
2775                                 'height': height
2776                         });
2777                 }
2779                 // Set width
2780                 this.windowEl.setStyle('width', width);
2781                 this.overlayEl.setStyle('width', width);
2782                 this.titleBarEl.setStyles({
2783                         'width': width - shadowBlur2x,
2784                         'height': options.headerHeight
2785                 });
2787                 // Draw Window
2788                 if (this.options.useCanvas != false) {
2789                         this.canvasEl.height = height;
2790                         this.canvasEl.width = width;
2792                         var ctx = this.canvasEl.getContext('2d');
2793                         ctx.clearRect(0, 0, width, height);
2795                         this.drawBoxCollapsed(ctx, width, height, shadowBlur, shadowOffset, shadows);
2796                         if (options.useCanvasControls == true) {
2797                                 this.drawControls(width, height, shadows);
2798                         }
2800                         // Invisible dummy object. The last element drawn is not rendered consistently while resizing in IE6 and IE7
2801                         if (MUI.ieLegacySupport){
2802                                 MUI.triangle(ctx, 0, 0, 10, 10, options.resizableColor, 0);
2803                         }
2804                 }
2806                 this.drawingWindow = false;
2807                 return this;
2809         },
2810         drawControls : function(width, height, shadows){
2811                 var options = this.options;
2812                 var shadowBlur = options.shadowBlur;
2813                 var shadowOffset = options.shadowOffset;
2814                 var controlsOffset = options.controlsOffset;
2816                 // Make sure controls are placed correctly.
2817                 this.controlsEl.setStyles({
2818                         'right': shadowBlur + shadowOffset.x + controlsOffset.right,
2819                         'top': shadowBlur - shadowOffset.y + controlsOffset.top
2820                 });
2822                 this.canvasControlsEl.setStyles({
2823                         'right': shadowBlur + shadowOffset.x + controlsOffset.right,
2824                         'top': shadowBlur - shadowOffset.y + controlsOffset.top
2825                 });
2827                 // Calculate X position for controlbuttons
2828                 //var mochaControlsWidth = 52;
2829                 this.closebuttonX = options.closable ? this.mochaControlsWidth - 7 : this.mochaControlsWidth + 12;
2830                 this.maximizebuttonX = this.closebuttonX - (options.maximizable ? 19 : 0);
2831                 this.minimizebuttonX = this.maximizebuttonX - (options.minimizable ? 19 : 0);
2833                 var ctx2 = this.canvasControlsEl.getContext('2d');
2834                 ctx2.clearRect(0, 0, 100, 100);
2836                 if (this.options.closable){
2837                         this.closebutton(
2838                                 ctx2,
2839                                 this.closebuttonX,
2840                                 7,
2841                                 options.closeBgColor,
2842                                 1.0,
2843                                 options.closeColor,
2844                                 1.0
2845                         );
2846                 }
2847                 if (this.options.maximizable){
2848                         this.maximizebutton(
2849                                 ctx2,
2850                                 this.maximizebuttonX,
2851                                 7,
2852                                 options.maximizeBgColor,
2853                                 1.0,
2854                                 options.maximizeColor,
2855                                 1.0
2856                         );
2857                 }
2858                 if (this.options.minimizable){
2859                         this.minimizebutton(
2860                                 ctx2,
2861                                 this.minimizebuttonX,
2862                                 7,
2863                                 options.minimizeBgColor,
2864                                 1.0,
2865                                 options.minimizeColor,
2866                                 1.0
2867                         );
2868                 }
2869                                         // Invisible dummy object. The last element drawn is not rendered consistently while resizing in IE6 and IE7
2870                         if (MUI.ieLegacySupport){
2871                                 MUI.circle(ctx2, 0, 0, 3, this.options.resizableColor, 0);
2872                         }
2874         },
2875         drawBox: function(ctx, width, height, shadowBlur, shadowOffset, shadows){
2877                 var options = this.options;
2878                 var shadowBlur2x = shadowBlur * 2;
2879                 var cornerRadius = this.options.cornerRadius;
2881                 // This is the drop shadow. It is created onion style.
2882                 if ( shadows != false ) {
2883                         for (var x = 0; x <= shadowBlur; x++){
2884                                 MUI.roundedRect(
2885                                         ctx,
2886                                         shadowOffset.x + x,
2887                                         shadowOffset.y + x,
2888                                         width - (x * 2) - shadowOffset.x,
2889                                         height - (x * 2) - shadowOffset.y,
2890                                         cornerRadius + (shadowBlur - x),
2891                                         [0, 0, 0],
2892                                         x == shadowBlur ? .29 : .065 + (x * .01)
2893                                 );
2894                         }
2895                 }
2896                 // Window body.
2897                 this.bodyRoundedRect(
2898                         ctx,                          // context
2899                         shadowBlur - shadowOffset.x,  // x
2900                         shadowBlur - shadowOffset.y,  // y
2901                         width - shadowBlur2x,         // width
2902                         height - shadowBlur2x,        // height
2903                         cornerRadius,                 // corner radius
2904                         options.bodyBgColor      // Footer color
2905                 );
2907                 if (this.options.type != 'notification'){
2908                 // Window header.
2909                         this.topRoundedRect(
2910                                 ctx,                          // context
2911                                 shadowBlur - shadowOffset.x,  // x
2912                                 shadowBlur - shadowOffset.y,  // y
2913                                 width - shadowBlur2x,         // width
2914                                 options.headerHeight,         // height
2915                                 cornerRadius,                 // corner radius
2916                                 options.headerStartColor,     // Header gradient's top color
2917                                 options.headerStopColor       // Header gradient's bottom color
2918                         );
2919                 }
2920         },
2921         drawBoxCollapsed: function(ctx, width, height, shadowBlur, shadowOffset, shadows){
2923                 var options = this.options;
2924                 var shadowBlur2x = shadowBlur * 2;
2925                 var cornerRadius = options.cornerRadius;
2927                 // This is the drop shadow. It is created onion style.
2928                 if ( shadows != false ){
2929                         for (var x = 0; x <= shadowBlur; x++){
2930                                 MUI.roundedRect(
2931                                         ctx,
2932                                         shadowOffset.x + x,
2933                                         shadowOffset.y + x,
2934                                         width - (x * 2) - shadowOffset.x,
2935                                         height - (x * 2) - shadowOffset.y,
2936                                         cornerRadius + (shadowBlur - x),
2937                                         [0, 0, 0],
2938                                         x == shadowBlur ? .3 : .06 + (x * .01)
2939                                 );
2940                         }
2941                 }
2943                 // Window header
2944                 this.topRoundedRect2(
2945                         ctx,                          // context
2946                         shadowBlur - shadowOffset.x,  // x
2947                         shadowBlur - shadowOffset.y,  // y
2948                         width - shadowBlur2x,         // width
2949                         options.headerHeight + 2,     // height
2950                         cornerRadius,                 // corner radius
2951                         options.headerStartColor,     // Header gradient's top color
2952                         options.headerStopColor       // Header gradient's bottom color
2953                 );
2955         },
2956         drawGauge: function(ctx, width, height, shadowBlur, shadowOffset, shadows){
2957                 var options = this.options;
2958                 var radius = (width * .5) - (shadowBlur) + 16;
2959                 if (shadows != false) {
2960                         for (var x = 0; x <= shadowBlur; x++){
2961                                 MUI.circle(
2962                                         ctx,
2963                                         width * .5 + shadowOffset.x,
2964                                         (height  + options.headerHeight) * .5 + shadowOffset.x,
2965                                         (width *.5) - (x * 2) - shadowOffset.x,
2966                                         [0, 0, 0],
2967                                         x == shadowBlur ? .75 : .075 + (x * .04)
2968                                 );
2969                         }
2970                 }
2971                 MUI.circle(
2972                         ctx,
2973                         width * .5  - shadowOffset.x,
2974                         (height + options.headerHeight) * .5  - shadowOffset.y,
2975                         (width *.5) - shadowBlur,
2976                         options.bodyBgColor,
2977                         1
2978                 );
2980                 // Draw gauge header
2981                 this.canvasHeaderEl.setStyles({
2982                         'top': shadowBlur - shadowOffset.y,
2983                         'left': shadowBlur - shadowOffset.x
2984                 });
2985                 var ctx = this.canvasHeaderEl.getContext('2d');
2986                 ctx.clearRect(0, 0, width, 100);
2987                 ctx.beginPath();
2988                 ctx.lineWidth = 24;
2989                 ctx.lineCap = 'round';
2990                 ctx.moveTo(13, 13);
2991                 ctx.lineTo(width - (shadowBlur*2) - 13, 13);
2992                 ctx.strokeStyle = 'rgba(0, 0, 0, .65)';
2993                 ctx.stroke();
2994         },
2995         bodyRoundedRect: function(ctx, x, y, width, height, radius, rgb){
2996                 ctx.fillStyle = 'rgba(' + rgb.join(',') + ', 1)';
2997                 ctx.beginPath();
2998                 ctx.moveTo(x, y + radius);
2999                 ctx.lineTo(x, y + height - radius);
3000                 ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
3001                 ctx.lineTo(x + width - radius, y + height);
3002                 ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
3003                 ctx.lineTo(x + width, y + radius);
3004                 ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
3005                 ctx.lineTo(x + radius, y);
3006                 ctx.quadraticCurveTo(x, y, x, y + radius);
3007                 ctx.fill();
3009         },
3010         topRoundedRect: function(ctx, x, y, width, height, radius, headerStartColor, headerStopColor){
3011                 var lingrad = ctx.createLinearGradient(0, 0, 0, height);
3012                 lingrad.addColorStop(0, 'rgb(' + headerStartColor.join(',') + ')');
3013                 lingrad.addColorStop(1, 'rgb(' + headerStopColor.join(',') + ')');
3014                 ctx.fillStyle = lingrad;
3015                 ctx.beginPath();
3016                 ctx.moveTo(x, y);
3017                 ctx.lineTo(x, y + height);
3018                 ctx.lineTo(x + width, y + height);
3019                 ctx.lineTo(x + width, y + radius);
3020                 ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
3021                 ctx.lineTo(x + radius, y);
3022                 ctx.quadraticCurveTo(x, y, x, y + radius);
3023                 ctx.fill();
3025         },
3026         topRoundedRect2: function(ctx, x, y, width, height, radius, headerStartColor, headerStopColor){
3027                 // Chrome is having trouble rendering the LinearGradient in this particular case
3028                 if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
3029                         ctx.fillStyle = 'rgba(' + headerStopColor.join(',') + ', 1)';
3030                 }
3031                 else {
3032                         var lingrad = ctx.createLinearGradient(0, this.options.shadowBlur - 1, 0, height + this.options.shadowBlur + 3);
3033                         lingrad.addColorStop(0, 'rgb(' + headerStartColor.join(',') + ')');
3034                         lingrad.addColorStop(1, 'rgb(' + headerStopColor.join(',') + ')');
3035                         ctx.fillStyle = lingrad;
3036                 }
3037                 ctx.beginPath();
3038                 ctx.moveTo(x, y + radius);
3039                 ctx.lineTo(x, y + height - radius);
3040                 ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
3041                 ctx.lineTo(x + width - radius, y + height);
3042                 ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
3043                 ctx.lineTo(x + width, y + radius);
3044                 ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
3045                 ctx.lineTo(x + radius, y);
3046                 ctx.quadraticCurveTo(x, y, x, y + radius);
3047                 ctx.fill();
3048         },
3049         maximizebutton: function(ctx, x, y, rgbBg, aBg, rgb, a){
3050                 // Circle
3051                 ctx.beginPath();
3052                 ctx.arc(x, y, 7, 0, Math.PI*2, true);
3053                 ctx.fillStyle = 'rgba(' + rgbBg.join(',') + ',' + aBg + ')';
3054                 ctx.fill();
3055                 // X sign
3056                 ctx.strokeStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
3057                 ctx.lineWidth = 2;
3058                 ctx.beginPath();
3059                 ctx.moveTo(x, y - 3.5);
3060                 ctx.lineTo(x, y + 3.5);
3061                 ctx.moveTo(x - 3.5, y);
3062                 ctx.lineTo(x + 3.5, y);
3063                 ctx.stroke();
3064         },
3065         closebutton: function(ctx, x, y, rgbBg, aBg, rgb, a){
3066                 // Circle
3067                 ctx.beginPath();
3068                 ctx.arc(x, y, 7, 0, Math.PI*2, true);
3069                 ctx.fillStyle = 'rgba(' + rgbBg.join(',') + ',' + aBg + ')';
3070                 ctx.fill();
3071                 // Plus sign
3072                 ctx.strokeStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
3073                 ctx.lineWidth = 2;
3074                 ctx.beginPath();
3075                 ctx.moveTo(x - 3, y - 3);
3076                 ctx.lineTo(x + 3, y + 3);
3077                 ctx.moveTo(x + 3, y - 3);
3078                 ctx.lineTo(x - 3, y + 3);
3079                 ctx.stroke();
3080         },
3081         minimizebutton: function(ctx, x, y, rgbBg, aBg, rgb, a){
3082                 // Circle
3083                 ctx.beginPath();
3084                 ctx.arc(x,y,7,0,Math.PI*2,true);
3085                 ctx.fillStyle = 'rgba(' + rgbBg.join(',') + ',' + aBg + ')';
3086                 ctx.fill();
3087                 // Minus sign
3088                 ctx.strokeStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
3089                 ctx.lineWidth = 2;
3090                 ctx.beginPath();
3091                 ctx.moveTo(x - 3.5, y);
3092                 ctx.lineTo(x + 3.5, y);
3093                 ctx.stroke();
3094         },
3095         setMochaControlsWidth: function(){
3096                 this.mochaControlsWidth = 0;
3097                 var options = this.options;
3098                 if (options.minimizable){
3099                         this.mochaControlsWidth += (this.minimizeButtonEl.getStyle('margin-left').toInt() + this.minimizeButtonEl.getStyle('width').toInt());
3100                 }
3101                 if (options.maximizable){
3102                         this.mochaControlsWidth += (this.maximizeButtonEl.getStyle('margin-left').toInt() + this.maximizeButtonEl.getStyle('width').toInt());
3103                 }
3104                 if (options.closable){
3105                         this.mochaControlsWidth += (this.closeButtonEl.getStyle('margin-left').toInt() + this.closeButtonEl.getStyle('width').toInt());
3106                 }
3107                 this.controlsEl.setStyle('width', this.mochaControlsWidth);
3108                 if (options.useCanvasControls == true){
3109                         this.canvasControlsEl.setProperty('width', this.mochaControlsWidth);
3110                 }
3111         },
3112         /*
3114         Function: hideSpinner
3115                 Hides the spinner.
3117         Example:
3118                 (start code)
3119                 $('myWindow').retrieve('instance').hideSpinner();
3120                 (end)
3122         */
3123         hideSpinner: function() {
3124                 if (this.spinnerEl)     this.spinnerEl.hide();
3125                 return this;
3126         },
3127         /*
3129         Function: showSpinner
3130                 Shows the spinner.
3132         Example:
3133                 (start code)
3134                 $('myWindow').retrieve('instance').showSpinner();
3135                 (end)
3137         */
3138         showSpinner: function(){
3139                 if (this.spinnerEl) this.spinnerEl.show();
3140                 return this;
3141         },
3142         /*
3144         Function: close
3145                 Closes the window. This is an alternative to using MUI.Core.closeWindow().
3147         Example:
3148                 (start code)
3149                 $('myWindow').retrieve('instance').close();
3150                 (end)
3152          */
3153         close: function( ) {
3154                 if (!this.isClosing) MUI.closeWindow(this.windowEl);
3155                 return this;
3156         },
3157         /*
3159         Function: minimize
3160                 Minimizes the window.
3162         Example:
3163                 (start code)
3164                 $('myWindow').retrieve('instance').minimize();
3165                 (end)
3167          */
3168         minimize: function( ){
3169                 MUI.Dock.minimizeWindow(this.windowEl);
3170                 return this;
3171         },
3172         /*
3174         Function: maximize
3175                 Maximizes the window.
3177         Example:
3178                 (start code)
3179                 $('myWindow').retrieve('instance').maximize();
3180                 (end)
3182          */
3183         maximize: function( ) {
3184                 if (this.isMinimized){
3185                         MUI.Dock.restoreMinimized(this.windowEl);
3186                 }
3187                 MUI.Desktop.maximizeWindow(this.windowEl);
3188                 return this;
3189         },
3190         /*
3192         Function: restore
3193                 Restores a minimized/maximized window to its original size.
3195         Example:
3196                 (start code)
3197                 $('myWindow').retrieve('instance').restore();
3198                 (end)
3200          */
3201         restore: function() {
3202                 if ( this.isMinimized )
3203                         MUI.Dock.restoreMinimized(this.windowEl);
3204                 else if ( this.isMaximized )
3205                         MUI.Desktop.restoreWindow(this.windowEl);
3206                 return this;
3207         },
3208         /*
3210         Function: resize
3211                 Resize a window.
3213         Notes:
3214                 If Advanced Effects are on the resize is animated. If centered is set to true the window remains centered as it resizes.
3216         Example:
3217                 (start code)
3218                 $('myWindow').retrieve('instance').resize({width:500,height:300,centered:true});
3219                 (end)
3221          */
3222         resize: function(options){
3223                 MUI.resizeWindow(this.windowEl, options);
3224                 return this;
3225         },
3226         /*
3228         Function: center
3229                 Center a window.
3231         Example:
3232                 (start code)
3233                 $('myWindow').retrieve('instance').center();
3234                 (end)
3236          */
3237         center: function() {
3238                 MUI.centerWindow(this.windowEl);
3239                 return this;
3240         },
3242         hide: function(){
3243                 this.windowEl.setStyle('display', 'none');
3244                 return this;
3245         },
3247         show: function(){
3248                 this.windowEl.setStyle('display', 'block');
3249                 return this;
3250         }
3254 MUI.extend({
3255         /*
3257         Function: closeWindow
3258                 Closes a window.
3260         Syntax:
3261         (start code)
3262                 MUI.closeWindow();
3263         (end)
3265         Arguments:
3266                 windowEl - the ID of the window to be closed
3268         Returns:
3269                 true - the window was closed
3270                 false - the window was not closed
3272         */
3273         closeWindow: function(windowEl){
3275                 var instance = windowEl.retrieve('instance');
3277                 // Does window exist and is not already in process of closing ?
3278                 if (windowEl != $(windowEl) || instance.isClosing) return;
3280                 instance.isClosing = true;
3281                 instance.fireEvent('onClose', windowEl);
3283                 if (instance.options.storeOnClose){
3284                         this.storeOnClose(instance, windowEl);
3285                         return;
3286                 }
3287                 if (instance.check) instance.check.destroy();
3289                 if ((instance.options.type == 'modal' || instance.options.type == 'modal2') && Browser.Engine.trident4){
3290                         $('modalFix').hide();
3291                 }
3293                 if (MUI.options.advancedEffects == false){
3294                         if (instance.options.type == 'modal' || instance.options.type == 'modal2'){
3295                                 $('modalOverlay').setStyle('opacity', 0);
3296                         }
3297                         MUI.closingJobs(windowEl);
3298                         return true;
3299                 }
3300                 else {
3301                         // Redraws IE windows without shadows since IE messes up canvas alpha when you change element opacity
3302                         if (MUI.ieLegacySupport) instance.drawWindow(false);
3303                         if (instance.options.type == 'modal' || instance.options.type == 'modal2'){
3304                                 MUI.Modal.modalOverlayCloseMorph.start({
3305                                         'opacity': 0
3306                                 });
3307                         }
3308                         var closeMorph = new Fx.Morph(windowEl, {
3309                                 duration: 120,
3310                                 onComplete: function(){
3311                                         MUI.closingJobs(windowEl);
3312                                         return true;
3313                                 }.bind(this)
3314                         });
3315                         closeMorph.start({
3316                                 'opacity': .4
3317                         });
3318                 }
3320         },
3321         closingJobs: function(windowEl){
3323                 var instances = MUI.Windows.instances;
3324                 var instance = instances.get(windowEl.id);
3325                 windowEl.setStyle('visibility', 'hidden');
3326                 // Destroy throws an error in IE8
3327                 if (MUI.ieLegacySupport) {
3328                         windowEl.dispose();
3329                 }
3330                 else {
3331                         windowEl.destroy();
3332                 }
3333                 instance.fireEvent('onCloseComplete');
3335                 if (instance.options.type != 'notification'){
3336                         var newFocus = this.getWindowWithHighestZindex();
3337                         this.focusWindow(newFocus);
3338                 }
3340                 instances.erase(instance.options.id);
3341                 if (this.loadingWorkspace == true) {
3342                         this.windowUnload();
3343                 }
3345                 if (MUI.Dock && $(MUI.options.dock) && instance.options.type == 'window') {
3346                         var currentButton = $(instance.options.id + '_dockTab');
3347                         if (currentButton != null) {
3348                                 MUI.Dock.dockSortables.removeItems(currentButton).destroy();
3349                         }
3350                         // Need to resize everything in case the dock becomes smaller when a tab is removed
3351                         MUI.Desktop.setDesktopSize();
3352                 }
3353         },
3354         storeOnClose: function(instance, windowEl){
3356                 if (instance.check) instance.check.hide();
3358                 windowEl.setStyles({
3359                         zIndex: -1
3360                 });
3361                 windowEl.addClass('windowClosed');
3362                 windowEl.removeClass('mocha');
3364                 if (MUI.Dock && $(MUI.options.dock) && instance.options.type == 'window') {
3365                         var currentButton = $(instance.options.id + '_dockTab');
3366                         if (currentButton != null) {
3367                                 currentButton.hide();
3368                         }
3369                         MUI.Desktop.setDesktopSize();
3370                 }
3372                 instance.fireEvent('onCloseComplete');
3374                 if (instance.options.type != 'notification'){
3375                         var newFocus = this.getWindowWithHighestZindex();
3376                         this.focusWindow(newFocus);
3377                 }
3379                 instance.isClosing = false;
3381         },
3382         /*
3384         Function: closeAll
3385                 Close all open windows.
3387         */
3388         closeAll: function() {
3389                 $$('.mocha').each(function(windowEl){
3390                         this.closeWindow(windowEl);
3391                 }.bind(this));
3392         },
3393         /*
3395         Function: collapseToggle
3396                 Collapses an expanded window. Expands a collapsed window.
3398         */
3399         collapseToggle: function(windowEl){
3400                 var instance = windowEl.retrieve('instance');
3401                 var handles = windowEl.getElements('.handle');
3402                 if (instance.isMaximized == true) return;
3403                 if (instance.isCollapsed == false) {
3404                         instance.isCollapsed = true;
3405                         handles.hide();
3406                         if ( instance.iframeEl ) {
3407                                 instance.iframeEl.setStyle('visibility', 'hidden');
3408                         }
3409                         instance.contentBorderEl.setStyles({
3410                                 visibility: 'hidden',
3411                                 position: 'absolute',
3412                                 top: -10000,
3413                                 left: -10000
3414                         });
3415                         if(instance.toolbarWrapperEl){
3416                                 instance.toolbarWrapperEl.setStyles({
3417                                         visibility: 'hidden',
3418                                         position: 'absolute',
3419                                         top: -10000,
3420                                         left: -10000
3421                                 });
3422                         }
3423                         instance.drawWindowCollapsed();
3424                 }
3425                 else {
3426                         instance.isCollapsed = false;
3427                         instance.drawWindow();
3428                         instance.contentBorderEl.setStyles({
3429                                 visibility: 'visible',
3430                                 position: null,
3431                                 top: null,
3432                                 left: null
3433                         });
3434                         if(instance.toolbarWrapperEl){
3435                                 instance.toolbarWrapperEl.setStyles({
3436                                         visibility: 'visible',
3437                                         position: null,
3438                                         top: null,
3439                                         left: null
3440                                 });
3441                         }
3442                         if ( instance.iframeEl ) {
3443                                 instance.iframeEl.setStyle('visibility', 'visible');
3444                         }
3445                         handles.show();
3446                 }
3447         },
3448         /*
3450         Function: toggleWindowVisibility
3451                 Toggle window visibility with Ctrl-Alt-Q.
3453         */
3454         toggleWindowVisibility: function(){
3455                 MUI.Windows.instances.each(function(instance){
3456                         if (instance.options.type == 'modal' || instance.options.type == 'modal2' || instance.isMinimized == true) return;
3457                         var id = $(instance.options.id);
3458                         if (id.getStyle('visibility') == 'visible'){
3459                                 if (instance.iframe){
3460                                         instance.iframeEl.setStyle('visibility', 'hidden');
3461                                 }
3462                                 if (instance.toolbarEl){
3463                                         instance.toolbarWrapperEl.setStyle('visibility', 'hidden');
3464                                 }
3465                                 instance.contentBorderEl.setStyle('visibility', 'hidden');
3466                                 id.setStyle('visibility', 'hidden');
3467                                 MUI.Windows.windowsVisible = false;
3468                         }
3469                         else {
3470                                 id.setStyle('visibility', 'visible');
3471                                 instance.contentBorderEl.setStyle('visibility', 'visible');
3472                                 if (instance.iframe){
3473                                         instance.iframeEl.setStyle('visibility', 'visible');
3474                                 }
3475                                 if (instance.toolbarEl){
3476                                         instance.toolbarWrapperEl.setStyle('visibility', 'visible');
3477                                 }
3478                                 MUI.Windows.windowsVisible = true;
3479                         }
3480                 }.bind(this));
3482         },
3483         focusWindow: function(windowEl, fireEvent){
3485                 // This is used with blurAll
3486                 MUI.Windows.focusingWindow = true;
3487                 var windowClicked = function(){
3488                         MUI.Windows.focusingWindow = false;
3489                 };
3490                 windowClicked.delay(170, this);
3492                 // Only focus when needed
3493                 if ($$('.mocha').length == 0) return;
3494                 if (windowEl != $(windowEl) || windowEl.hasClass('isFocused')) return;
3496                 var instances =  MUI.Windows.instances;
3497                 var instance = instances.get(windowEl.id);
3499                 if (instance.options.type == 'notification'){
3500                         windowEl.setStyle('zIndex', 11001);
3501                         return;
3502                 };
3504                 MUI.Windows.indexLevel += 2;
3505                 windowEl.setStyle('zIndex', MUI.Windows.indexLevel);
3507                 // Used when dragging and resizing windows
3508                 $('windowUnderlay').setStyle('zIndex', MUI.Windows.indexLevel - 1).inject($(windowEl),'after');
3510                 // Fire onBlur for the window that lost focus.
3511                 instances.each(function(instance){
3512                         if (instance.windowEl.hasClass('isFocused')){
3513                                 instance.fireEvent('onBlur', instance.windowEl);
3514                         }
3515                         instance.windowEl.removeClass('isFocused');
3516                 });
3518                 if (MUI.Dock && $(MUI.options.dock) && instance.options.type == 'window') {
3519                         MUI.Dock.makeActiveTab();
3520                 }
3521                 windowEl.addClass('isFocused');
3523                 if (fireEvent != false){
3524                         instance.fireEvent('onFocus', windowEl);
3525                 }
3527         },
3528         getWindowWithHighestZindex: function(){
3529                 this.highestZindex = 0;
3530                 $$('.mocha').each(function(element){
3531                         this.zIndex = element.getStyle('zIndex');
3532                         if (this.zIndex >= this.highestZindex) {
3533                                 this.highestZindex = this.zIndex;
3534                         }
3535                 }.bind(this));
3536                 $$('.mocha').each(function(element){
3537                         if (element.getStyle('zIndex') == this.highestZindex) {
3538                                 this.windowWithHighestZindex = element;
3539                         }
3540                 }.bind(this));
3541                 return this.windowWithHighestZindex;
3542         },
3543         blurAll: function(){
3544                 if (MUI.Windows.focusingWindow == false) {
3545                         $$('.mocha').each(function(windowEl){
3546                                 var instance = windowEl.retrieve('instance');
3547                                 if (instance.options.type != 'modal' && instance.options.type != 'modal2'){
3548                                         windowEl.removeClass('isFocused');
3549                                 }
3550                         });
3551                         $$('.dockTab').removeClass('activeDockTab');
3552                 }
3553         },
3554         centerWindow: function(windowEl){
3556                 if(!windowEl){
3557                         MUI.Windows.instances.each(function(instance){
3558                                 if (instance.windowEl.hasClass('isFocused')){
3559                                         windowEl = instance.windowEl;
3560                                 }
3561                         });
3562                 }
3564                 var instance = windowEl.retrieve('instance');
3565                 var options = instance.options;
3566                 var dimensions = options.container.getCoordinates();
3568                 var windowPosTop = window.getScroll().y + (window.getSize().y * .5) - (windowEl.offsetHeight * .5);
3569                 if (windowPosTop < -instance.options.shadowBlur){
3570                         windowPosTop = -instance.options.shadowBlur;
3571                 }
3572                 var windowPosLeft =     (dimensions.width * .5) - (windowEl.offsetWidth * .5);
3573                 if (windowPosLeft < -instance.options.shadowBlur){
3574                         windowPosLeft = -instance.options.shadowBlur;
3575                 }
3576                 if (MUI.options.advancedEffects == true){
3577                         instance.morph.start({
3578                                 'top': windowPosTop,
3579                                 'left': windowPosLeft
3580                         });
3581                 }
3582                 else {
3583                         windowEl.setStyles({
3584                                 'top': windowPosTop,
3585                                 'left': windowPosLeft
3586                         });
3587                 }
3588         },
3589         resizeWindow: function(windowEl, options){
3590                 var instance = windowEl.retrieve('instance');
3592                 $extend({
3593                         width: null,
3594                         height: null,
3595                         top: null,
3596                         left: null,
3597                         centered: true
3598                 }, options);
3600                 var oldWidth = windowEl.getStyle('width').toInt();
3601                 var oldHeight = windowEl.getStyle('height').toInt();
3602                 var oldTop = windowEl.getStyle('top').toInt();
3603                 var oldLeft = windowEl.getStyle('left').toInt();
3605                 if (options.centered){
3606                         var top = options.top || oldTop - ((options.height - oldHeight) * .5);
3607                         var left = options.left || oldLeft - ((options.width - oldWidth) * .5);
3608                 }
3609                 else {
3610                         var top =  options.top || oldTop;
3611                         var left = options.left || oldLeft;
3612                 }
3614                 if (MUI.options.advancedEffects == false){
3615                         windowEl.setStyles({
3616                                 'top': top,
3617                                 'left': left
3618                         });
3619                         instance.contentWrapperEl.setStyles({
3620                                 'height': options.height,
3621                                 'width':  options.width
3622                         });
3623                         instance.drawWindow();
3624                         // Show iframe
3625                         if (instance.iframeEl){
3626                                 if (!MUI.ieLegacySupport) {
3627                                         instance.iframeEl.setStyle('visibility', 'visible');
3628                                 }
3629                                 else {
3630                                         instance.iframeEl.show();
3631                                 }
3632                         }
3633                 }
3634                 else {
3635                         windowEl.retrieve('resizeMorph').start({
3636                                 '0': {  'height': options.height,
3637                                                 'width':  options.width
3638                                 },
3639                                 '1': {  'top': top,
3640                                                 'left': left
3641                                 }
3642                         });
3643                 }
3644                 return instance;
3645         },
3646         /*
3648         Internal Function: dynamicResize
3649                 Use with a timer to resize a window as the window's content size changes, such as with an accordion.
3651         */
3652         dynamicResize: function(windowEl){
3653                 var instance = windowEl.retrieve('instance');
3654                 var contentWrapperEl = instance.contentWrapperEl;
3655                 var contentEl = instance.contentEl;
3657                 contentWrapperEl.setStyles({
3658                         'height': contentEl.offsetHeight,
3659                         'width': contentEl.offsetWidth
3660                 });
3661                 instance.drawWindow();
3662         }
3665 // Toggle window visibility with Ctrl-Alt-Q
3666 document.addEvent('keydown', function(event){
3667         if (event.key == 'q' && event.control && event.alt) {
3668                 MUI.toggleWindowVisibility();
3669         }
3673 Script: Modal.js
3674         Create modal dialog windows.
3676 Copyright:
3677         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
3679 License:
3680         MIT-style license.
3682 Requires:
3683         Core.js, Window.js
3685 See Also:
3686         <Window>
3690 MUI.files[MUI.path.source + 'Window/Modal.js'] = 'loaded';
3692 MUI.Modal = new Class({
3694         Extends: MUI.Window,
3696         options: {
3697                 type: 'modal'
3698         },
3700         initialize: function(options){
3702                 if (!$('modalOverlay')){
3703                         this.modalInitialize();
3705                         window.addEvent('resize', function(){
3706                                 this.setModalSize();
3707                         }.bind(this));
3708                 }
3709                 this.parent(options);
3711         },
3712         modalInitialize: function(){
3713                 var modalOverlay = new Element('div', {
3714                         'id': 'modalOverlay',
3715                         'styles': {
3716                                 'height': document.getCoordinates().height,
3717                                 'opacity': .6
3718                         }
3719                 }).inject(document.body);
3721                 modalOverlay.setStyles({
3722                                 'position': Browser.Engine.trident4 ? 'absolute' : 'fixed'
3723                 });
3725                 modalOverlay.addEvent('click', function(e){
3726                         var instance = MUI.Windows.instances.get(MUI.currentModal.id);
3727                         if (instance.options.modalOverlayClose == true) {
3728                                 MUI.closeWindow(MUI.currentModal);
3729                         }
3730                 });
3732                 if (Browser.Engine.trident4){
3733                         var modalFix = new Element('iframe', {
3734                                 'id': 'modalFix',
3735                                 'scrolling': 'no',
3736                                 'marginWidth': 0,
3737                                 'marginHeight': 0,
3738                                 'src': '',
3739                                 'styles': {
3740                                         'height': document.getCoordinates().height
3741                                 }
3742                         }).inject(document.body);
3743                 }
3745                 MUI.Modal.modalOverlayOpenMorph = new Fx.Morph($('modalOverlay'), {
3746                         'duration': 150
3747                 });
3748                 MUI.Modal.modalOverlayCloseMorph = new Fx.Morph($('modalOverlay'), {
3749                         'duration': 150,
3750                         onComplete: function(){
3751                                 $('modalOverlay').hide();
3752                                 if (Browser.Engine.trident4){
3753                                         $('modalFix').hide();
3754                                 }
3755                         }.bind(this)
3756                 });
3757         },
3758         setModalSize: function(){
3759                 $('modalOverlay').setStyle('height', document.getCoordinates().height);
3760                 if (Browser.Engine.trident4){
3761                         $('modalFix').setStyle('height', document.getCoordinates().height);
3762                 }
3763         }
3768 Script: Windows-from-html.js
3769         Create windows from html markup in page.
3771 Copyright:
3772         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
3774 License:
3775         MIT-style license.
3777 Requires:
3778         Core.js, Window.js
3780 Example:
3781         HTML markup.
3782         (start code)
3783 <div class="mocha" id="mywindow" style="width:300px;height:255px;top:50px;left:350px">
3784         <h3 class="mochaTitle">My Window</h3>
3785         <p>My Window Content</p>
3786 </div>
3787         (end)
3789 See Also:
3790         <Window>
3794 MUI.files[MUI.path.source + 'Window/Windows-from-html.js'] = 'loaded';
3796 MUI.extend({
3797         NewWindowsFromHTML: function(){
3798                 $$('.mocha').each(function(el) {
3799                         // Get the window title and destroy that element, so it does not end up in window content
3800                         if ( Browser.Engine.presto || Browser.Engine.trident5 ){
3801                                 el.hide(); // Required by Opera, and probably IE7
3802                         }
3803                         var title = el.getElement('h3.mochaTitle');
3805                         if(Browser.Engine.presto) el.show();
3807                         var elDimensions = el.getStyles('height', 'width');
3808                         var properties = {
3809                                 id: el.getProperty('id'),
3810                                 height: elDimensions.height.toInt(),
3811                                 width: elDimensions.width.toInt(),
3812                                 x: el.getStyle('left').toInt(),
3813                                 y: el.getStyle('top').toInt()
3814                         };
3815                         // If there is a title element, set title and destroy the element so it does not end up in window content
3816                         if ( title ) {
3817                                 properties.title = title.innerHTML;
3818                                 title.destroy();
3819                         }
3821                         // Get content and destroy the element
3822                         properties.content = el.innerHTML;
3823                         el.destroy();
3825                         // Create window
3826                         new MUI.Window(properties, true);
3827                 }.bind(this));
3828         }
3832 Script: Windows-from-json.js
3833         Create one or more windows from JSON data. You can define all the same properties as you can for new MUI.Window(). Undefined properties are set to their defaults.
3835 Copyright:
3836         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
3838 License:
3839         MIT-style license.
3841 Syntax:
3842         (start code)
3843         MUI.newWindowsFromJSON(properties);
3844         (end)
3846 Example:
3847         (start code)
3848         MUI.jsonWindows = function(){
3849                 var url = 'data/json-windows-data.js';
3850                 var request = new Request.JSON({
3851                         url: url,
3852                         method: 'get',
3853                         onComplete: function(properties) {
3854                                 MUI.newWindowsFromJSON(properties.windows);
3855                         }
3856                 }).send();
3857         }
3858         (end)
3860 Note:
3861         Windows created from JSON are not compatible with the current cookie based version
3862         of Save and Load Workspace.
3864 See Also:
3865         <Window>
3869 MUI.files[MUI.path.source + 'Window/Windows-from-json.js'] = 'loaded';
3871 MUI.extend({
3872         newWindowsFromJSON: function(newWindows){
3873                 newWindows.each(function(options) {
3874                         var temp = new Hash(options);
3875                         temp.each( function(value, key, hash) {
3876                                 if ($type(value) != 'string') return;
3877                                 if (value.substring(0,8) == 'function'){
3878                                         eval("options." + key + " = " + value);
3879                                 }
3880                         });
3881                         new MUI.Window(options);
3882                 });
3883         }
3887 Script: Arrange-cascade.js
3888         Cascade windows.
3890 Copyright:
3891         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
3893 License:
3894         MIT-style license.
3896 Requires:
3897         Core.js, Window.js
3899 Syntax:
3900         (start code)
3901         MUI.arrangeCascade();
3902         (end)
3906 MUI.files[MUI.path.source + 'Window/Arrange-cascade.js'] = 'loaded';
3908 MUI.extend({
3909         arrangeCascade: function(){
3911                 var     viewportTopOffset = 30;    // Use a negative number if necessary to place first window where you want it
3912         var viewportLeftOffset = 20;
3913         var windowTopOffset = 50;    // Initial vertical spacing of each window
3914         var windowLeftOffset = 40;
3916                 // See how much space we have to work with
3917                 var coordinates = document.getCoordinates();
3919                 var openWindows = 0;
3920                 MUI.Windows.instances.each(function(instance){
3921                         if (!instance.isMinimized && instance.options.draggable) openWindows ++;
3922                 });
3924                 if ((windowTopOffset * (openWindows + 1)) >= (coordinates.height - viewportTopOffset)) {
3925                         var topOffset = (coordinates.height - viewportTopOffset) / (openWindows + 1);
3926                 }
3927                 else {
3928                         var topOffset = windowTopOffset;
3929                 }
3931                 if ((windowLeftOffset * (openWindows + 1)) >= (coordinates.width - viewportLeftOffset - 20)) {
3932                         var leftOffset = (coordinates.width - viewportLeftOffset - 20) / (openWindows + 1);
3933                 }
3934                 else {
3935                         var leftOffset = windowLeftOffset;
3936                 }
3938                 var x = viewportLeftOffset;
3939                 var y = viewportTopOffset;
3940                 $$('.mocha').each(function(windowEl){
3941                         var instance = windowEl.retrieve('instance');
3942                         if (!instance.isMinimized && !instance.isMaximized && instance.options.draggable){
3943                                 id = windowEl.id;
3944                                 MUI.focusWindow(windowEl);
3945                                 x += leftOffset;
3946                                 y += topOffset;
3948                                 if (MUI.options.advancedEffects == false){
3949                                         windowEl.setStyles({
3950                                                 'top': y,
3951                                                 'left': x
3952                                         });
3953                                 }
3954                                 else {
3955                                         var cascadeMorph = new Fx.Morph(windowEl, {
3956                                                 'duration': 550
3957                                         });
3958                                         cascadeMorph.start({
3959                                                 'top': y,
3960                                                 'left': x
3961                                         });
3962                                 }
3963                         }
3964                 }.bind(this));
3965         }
3969 Script: Arrange-tile.js
3970         Cascade windows.
3972 Copyright:
3973         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
3975 Authors:
3976         Harry Roberts and Greg Houston
3978 License:
3979         MIT-style license.
3981 Requires:
3982         Core.js, Window.js
3984 Syntax:
3985         (start code)
3986         MUI.arrangeTile();
3987         (end)
3991 MUI.files[MUI.path.source + 'Window/Arrange-tile.js'] = 'loaded';
3993 MUI.extend({
3994         arrangeTile: function(){
3996                 var     viewportTopOffset = 30;    // Use a negative number if necessary to place first window where you want it
3997                 var viewportLeftOffset = 20;
3999                 var x = 10;
4000                 var y = 80;
4002                 var instances =  MUI.Windows.instances;
4004                 var windowsNum = 0;
4006                 instances.each(function(instance){
4007                         if (!instance.isMinimized && !instance.isMaximized){
4008                                 windowsNum++;
4009                         }
4010                 });
4012                 var cols = 3;
4013                 var rows = Math.ceil(windowsNum / cols);
4015                 var coordinates = document.getCoordinates();
4017                 var col_width = ((coordinates.width - viewportLeftOffset) / cols);
4018                 var col_height = ((coordinates.height - viewportTopOffset) / rows);
4020                 var row = 0;
4021                 var col = 0;
4023                 instances.each(function(instance){
4024                         if (!instance.isMinimized && !instance.isMaximized && instance.options.draggable ){
4026                                 var content = instance.contentWrapperEl;
4027                                 var content_coords = content.getCoordinates();
4028                                 var window_coords = instance.windowEl.getCoordinates();
4030                                 // Calculate the amount of padding around the content window
4031                                 var padding_top = content_coords.top - window_coords.top;
4032                                 var padding_bottom = window_coords.height - content_coords.height - padding_top;
4033                                 var padding_left = content_coords.left - window_coords.left;
4034                                 var padding_right = window_coords.width - content_coords.width - padding_left;
4036                                 /*
4038                                 // This resizes the windows
4039                                 if (instance.options.shape != 'gauge' && instance.options.resizable == true){
4040                                         var width = (col_width - 3 - padding_left - padding_right);
4041                                         var height = (col_height - 3 - padding_top - padding_bottom);
4043                                         if (width > instance.options.resizeLimit.x[0] && width < instance.options.resizeLimit.x[1]){
4044                                                 content.setStyle('width', width);
4045                                         }
4046                                         if (height > instance.options.resizeLimit.y[0] && height < instance.options.resizeLimit.y[1]){
4047                                                 content.setStyle('height', height);
4048                                         }
4050                                 }*/
4052                                 var left = (x + (col * col_width));
4053                                 var top = (y + (row * col_height));
4055                                 instance.drawWindow();
4057                                 MUI.focusWindow(instance.windowEl);
4059                                 if (MUI.options.advancedEffects == false){
4060                                         instance.windowEl.setStyles({
4061                                                 'top': top,
4062                                                 'left': left
4063                                         });
4064                                 }
4065                                 else {
4066                                         var tileMorph = new Fx.Morph(instance.windowEl, {
4067                                                 'duration': 550
4068                                         });
4069                                         tileMorph.start({
4070                                                 'top': top,
4071                                                 'left': left
4072                                         });
4073                                 }
4075                                 if (++col === cols) {
4076                                         row++;
4077                                         col = 0;
4078                                 }
4079                         }
4080                 }.bind(this));
4081         }
4085 Script: Tabs.js
4086         Functionality for window tabs.
4088 Copyright:
4089         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
4091 License:
4092         MIT-style license.
4094 Requires:
4095         Core.js, Window.js (for tabbed windows) or Layout.js (for tabbed panels)
4099 MUI.files[MUI.path.source + 'Components/Tabs.js'] = 'loaded';
4101 MUI.extend({
4102         /*
4104         Function: initializeTabs
4105                 Add click event to each list item that fires the selected function.
4107         */
4108         initializeTabs: function(el){
4109                 $(el).setStyle('list-style', 'none'); // This is to fix a glitch that occurs in IE8 RC1 when dynamically switching themes
4110                 $(el).getElements('li').addEvent('click', function(e){
4111                         MUI.selected(this, el);
4112                 });
4113         },
4114         /*
4116         Function: selected
4117                 Add "selected" class to current list item and remove it from sibling list items.
4119         Syntax:
4120                 (start code)
4121                         selected(el, parent);
4122                 (end)
4124         Arguments:
4125                 el - the list item
4126                 parent - the ul
4128         */
4129         selected: function(el, parent){
4130                 $(parent).getChildren().each(function(listitem){
4131                         listitem.removeClass('selected');
4132                 });
4133                 el.addClass('selected');
4134         }
4139 Script: Layout.js
4140         Create web application layouts. Enables window maximize.
4142 Copyright:
4143         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
4145 License:
4146         MIT-style license.
4148 Requires:
4149         Core.js
4153 MUI.files[MUI.path.source + 'Layout/Layout.js'] = 'loaded';
4155 MUI.extend({
4156         Columns: {
4157                 instances: new Hash(),
4158                 columnIDCount: 0 // Used for columns without an ID defined by the user
4159         },
4160         Panels: {
4161                 instances: new Hash(),
4162                 panelIDCount: 0 // Used for panels without an ID defined by the user
4163         }
4166 MUI.Desktop = {
4168         options: {
4169                 // Naming options:
4170                 // If you change the IDs of the MochaUI Desktop containers in your HTML, you need to change them here as well.
4171                 desktop:             'desktop',
4172                 desktopHeader:       'desktopHeader',
4173                 desktopFooter:       'desktopFooter',
4174                 desktopNavBar:       'desktopNavbar',
4175                 pageWrapper:         'pageWrapper',
4176                 page:                'page',
4177                 desktopFooter:       'desktopFooterWrapper'
4178         },
4179         initialize: function(){
4181                 this.desktop         = $(this.options.desktop);
4182                 this.desktopHeader   = $(this.options.desktopHeader);
4183                 this.desktopNavBar   = $(this.options.desktopNavBar);
4184                 this.pageWrapper     = $(this.options.pageWrapper);
4185                 this.page            = $(this.options.page);
4186                 this.desktopFooter   = $(this.options.desktopFooter);
4188                 if (this.desktop) {
4189                         ($$('body')).setStyles({
4190                                 overflow: 'hidden',
4191                                 height: '100%',
4192                                 margin: 0
4193                         });
4194                         ($$('html')).setStyles({
4195                                 overflow: 'hidden',
4196                                 height: '100%'
4197                         });
4198                 }
4200                 // This is run on dock initialize so no need to do it twice.
4201                 if (!MUI.Dock){
4202                         this.setDesktopSize();
4203                 }
4204                 this.menuInitialize();
4206                 // Resize desktop, page wrapper, modal overlay, and maximized windows when browser window is resized
4207                 window.addEvent('resize', function(e){
4208                         this.onBrowserResize();
4209                 }.bind(this));
4211                 if (MUI.myChain){
4212                         MUI.myChain.callChain();
4213                 }
4215         },
4216         menuInitialize: function(){
4217                 // Fix for dropdown menus in IE6
4218                 if (Browser.Engine.trident4 && this.desktopNavBar){
4219                         this.desktopNavBar.getElements('li').each(function(element) {
4220                                 element.addEvent('mouseenter', function(){
4221                                         this.addClass('ieHover');
4222                                 });
4223                                 element.addEvent('mouseleave', function(){
4224                                         this.removeClass('ieHover');
4225                                 });
4226                         });
4227                 };
4228         },
4229         onBrowserResize: function(){
4230                 this.setDesktopSize();
4231                 // Resize maximized windows to fit new browser window size
4232                 setTimeout( function(){
4233                         MUI.Windows.instances.each(function(instance){
4234                                 if (instance.isMaximized){
4236                                         // Hide iframe while resize for better performance
4237                                         if ( instance.iframeEl ){
4238                                                 instance.iframeEl.setStyle('visibility', 'hidden');
4239                                         }
4241                                         var coordinates = document.getCoordinates();
4242                                         var borderHeight = instance.contentBorderEl.getStyle('margin-top').toInt() + instance.contentBorderEl.getStyle('margin-bottom').toInt();
4243                                         var toolbarHeight = instance.toolbarWrapperEl ? instance.toolbarWrapperEl.getStyle('height').toInt() + instance.toolbarWrapperEl.getStyle('margin-top').toInt() : 0;
4244                                         instance.contentWrapperEl.setStyles({
4245                                                 'height': coordinates.height - instance.options.headerHeight - instance.options.footerHeight - borderHeight - toolbarHeight,
4246                                                 'width': coordinates.width
4247                                         });
4249                                         instance.drawWindow();
4250                                         if ( instance.iframeEl ){
4251                                                 instance.iframeEl.setStyles({
4252                                                         'height': instance.contentWrapperEl.getStyle('height')
4253                                                 });
4254                                                 instance.iframeEl.setStyle('visibility', 'visible');
4255                                         }
4257                                 }
4258                         }.bind(this));
4259                 }.bind(this), 100);
4260         },
4261         setDesktopSize: function(){
4262                 var windowDimensions = window.getCoordinates();
4264                 // var dock = $(MUI.options.dock);
4265                 var dockWrapper = $(MUI.options.dockWrapper);
4267                 // Setting the desktop height may only be needed by IE7
4268                 if (this.desktop){
4269                         this.desktop.setStyle('height', windowDimensions.height);
4270                 }
4272                 // Set pageWrapper height so the dock doesn't cover the pageWrapper scrollbars.
4273                 if (this.pageWrapper) {
4274                         var dockOffset = MUI.dockVisible ? dockWrapper.offsetHeight : 0;
4275                         var pageWrapperHeight = windowDimensions.height;
4276                         pageWrapperHeight -= this.pageWrapper.getStyle('margin-top').toInt();
4277                         pageWrapperHeight -= this.pageWrapper.getStyle('margin-bottom').toInt();
4278                         if (this.desktopHeader){ pageWrapperHeight -= this.desktopHeader.offsetHeight; }
4279                         if (this.desktopFooter){ pageWrapperHeight -= this.desktopFooter.offsetHeight; }
4280                         pageWrapperHeight -= dockOffset;
4282                         if (pageWrapperHeight < 0){
4283                                 pageWrapperHeight = 0;
4284                         }
4285                         this.pageWrapper.setStyle('height', pageWrapperHeight);
4286                 }
4288                 if (MUI.Columns.instances.getKeys().length > 0){ // Conditional is a fix for a bug in IE6 in the no toolbars demo.
4289                         MUI.Desktop.resizePanels();
4290                 }
4291         },
4292         resizePanels: function(){
4293                 MUI.panelHeight();
4294                 MUI.rWidth();
4295         },
4296         /*
4298         Function: maximizeWindow
4299                 Maximize a window.
4301         Syntax:
4302                 (start code)
4303                 MUI.Desktop.maximizeWindow(windowEl);
4304                 (end)
4306         */
4307         maximizeWindow: function(windowEl){
4309                 var instance = MUI.Windows.instances.get(windowEl.id);
4310                 var options = instance.options;
4311                 var windowDrag = instance.windowDrag;
4313                 // If window no longer exists or is maximized, stop
4314                 if (windowEl != $(windowEl) || instance.isMaximized ) return;
4316                 if (instance.isCollapsed){
4317                         MUI.collapseToggle(windowEl);
4318                 }
4320                 instance.isMaximized = true;
4322                 // If window is restricted to a container, it should not be draggable when maximized.
4323                 if (instance.options.restrict){
4324                         windowDrag.detach();
4325                         if (options.resizable) {
4326                                 instance.detachResizable();
4327                         }
4328                         instance.titleBarEl.setStyle('cursor', 'default');
4329                 }
4331                 // If the window has a container that is not the desktop
4332                 // temporarily move the window to the desktop while it is minimized.
4333                 if (options.container != this.desktop){
4334                         this.desktop.grab(windowEl);
4335                         if (this.options.restrict){
4336                         windowDrag.container = this.desktop;
4337                         }
4338                 }
4340                 // Save original position
4341                 instance.oldTop = windowEl.getStyle('top');
4342                 instance.oldLeft = windowEl.getStyle('left');
4344                 var contentWrapperEl = instance.contentWrapperEl;
4346                 // Save original dimensions
4347                 contentWrapperEl.oldWidth = contentWrapperEl.getStyle('width');
4348                 contentWrapperEl.oldHeight = contentWrapperEl.getStyle('height');
4350                 // Hide iframe
4351                 // Iframe should be hidden when minimizing, maximizing, and moving for performance and Flash issues
4352                 if ( instance.iframeEl ) {
4353                         if (!MUI.ieLegacySupport) {
4354                                 instance.iframeEl.setStyle('visibility', 'hidden');
4355                         }
4356                         else {
4357                                 instance.iframeEl.hide();
4358                         }
4359                 }
4361                 var windowDimensions = document.getCoordinates();
4362                 var options = instance.options;
4363                 var shadowBlur = options.shadowBlur;
4364                 var shadowOffset = options.shadowOffset;
4365                 var newHeight = windowDimensions.height - options.headerHeight - options.footerHeight;
4366                 newHeight -= instance.contentBorderEl.getStyle('margin-top').toInt();
4367                 newHeight -= instance.contentBorderEl.getStyle('margin-bottom').toInt();
4368                 newHeight -= (instance.toolbarWrapperEl ? instance.toolbarWrapperEl.getStyle('height').toInt() + instance.toolbarWrapperEl.getStyle('margin-top').toInt() : 0);
4370                 MUI.resizeWindow(windowEl, {
4371                         width: windowDimensions.width,
4372                         height: newHeight,
4373                         top: shadowOffset.y - shadowBlur,
4374                         left: shadowOffset.x - shadowBlur
4375                 });
4376                 instance.fireEvent('onMaximize', windowEl);
4378                 if (instance.maximizeButtonEl) {
4379                         instance.maximizeButtonEl.setProperty('title', 'Restore');
4380                 }
4381                 MUI.focusWindow(windowEl);
4383         },
4384         /*
4386         Function: restoreWindow
4387                 Restore a maximized window.
4389         Syntax:
4390                 (start code)
4391                 MUI.Desktop.restoreWindow(windowEl);
4392                 (end)
4394         */
4395         restoreWindow: function(windowEl){
4397                 var instance = windowEl.retrieve('instance');
4399                 // Window exists and is maximized ?
4400                 if (windowEl != $(windowEl) || !instance.isMaximized) return;
4402                 var options = instance.options;
4403                 instance.isMaximized = false;
4405                 if (options.restrict){
4406                         instance.windowDrag.attach();
4407                         if (options.resizable){
4408                                 instance.reattachResizable();
4409                         }
4410                         instance.titleBarEl.setStyle('cursor', 'move');
4411                 }
4413                 // Hide iframe
4414                 // Iframe should be hidden when minimizing, maximizing, and moving for performance and Flash issues
4415                 if ( instance.iframeEl ) {
4416                         if (!MUI.ieLegacySupport) {
4417                                 instance.iframeEl.setStyle('visibility', 'hidden');
4418                         }
4419                         else {
4420                                 instance.iframeEl.hide();
4421                         }
4422                 }
4424                 var contentWrapperEl = instance.contentWrapperEl;
4426                 MUI.resizeWindow(windowEl,{
4427                         width: contentWrapperEl.oldWidth,
4428                         height: contentWrapperEl.oldHeight,
4429                         top: instance.oldTop,
4430                         left: instance.oldLeft
4431                 });
4432                 instance.fireEvent('onRestore', windowEl);
4434                 if (instance.maximizeButtonEl){
4435                         instance.maximizeButtonEl.setProperty('title', 'Maximize');
4436                 }
4437         }
4442 Class: Column
4443         Create a column. Columns should be created from left to right.
4445 Syntax:
4446 (start code)
4447         MUI.Column();
4448 (end)
4450 Arguments:
4451         options
4453 Options:
4454         id - The ID of the column. This must be set when creating the column.
4455         container - Defaults to MUI.Desktop.pageWrapper.
4456         placement - Can be 'right', 'main', or 'left'. There must be at least one column with the 'main' option.
4457         width - 'main' column is fluid and should not be given a width.
4458         resizeLimit - resizelimit of a 'right' or 'left' column.
4459         sortable - (boolean) Whether the panels can be reordered via drag and drop.
4460         onResize - (function) Fired when the column is resized.
4461         onCollapse - (function) Fired when the column is collapsed.
4462         onExpand - (function) Fired when the column is expanded.
4465 MUI.Column = new Class({
4467         Implements: [Events, Options],
4469         options: {
4470                 id:            null,
4471                 container:     null,
4472                 placement:     null,
4473                 width:         null,
4474                 resizeLimit:   [],
4475                 sortable:      true,
4477                 // Events
4478                 onResize:     $empty,
4479                 onCollapse:   $empty,
4480                 onExpand:     $empty
4482         },
4484         initialize: function(options){
4485                 this.setOptions(options);
4487                 $extend(this, {
4488                         timestamp: $time(),
4489                         isCollapsed: false,
4490                         oldWidth: 0
4491                 });
4493                 // If column has no ID, give it one.
4494                 if (this.options.id == null){
4495                         this.options.id = 'column' + (++MUI.Columns.columnIDCount);
4496                 }
4498                 // Shorten object chain
4499                 var options = this.options;
4500                 var instances = MUI.Columns.instances;
4501                 var instanceID = instances.get(options.id);
4503                 if (options.container == null) {
4504                         options.container = MUI.Desktop.pageWrapper
4505                 }
4506                 else {
4507                         $(options.container).setStyle('overflow', 'hidden');
4508                 }
4510                 if (typeof this.options.container == 'string'){
4511                         this.options.container = $(this.options.container);
4512                 }
4514                 // Check to see if there is already a class instance for this Column
4515                 if (instanceID){
4516                         var instance = instanceID;
4517                 }
4519                 // Check if column already exists
4520                 if ( this.columnEl ){
4521                         return;
4522                 }
4523                 else {
4524                         instances.set(options.id, this);
4525                 }
4527                 // If loading columns into a panel, hide the regular content container.
4528                 if ($(options.container).getElement('.pad') != null) {
4529                         $(options.container).getElement('.pad').hide();
4530                 }
4532                 // If loading columns into a window, hide the regular content container.
4533                 if ($(options.container).getElement('.mochaContent') != null) {
4534                         $(options.container).getElement('.mochaContent').hide();
4535                 }
4537                 this.columnEl = new Element('div', {
4538                         'id': this.options.id,
4539                         'class': 'column expanded',
4540                         'styles': {
4541                                 'width': options.placement == 'main' ? null : options.width
4542                         }
4543                 }).inject($(options.container));
4545                 this.columnEl.store('instance', this);
4547                 var parent = this.columnEl.getParent();
4548                 var columnHeight = parent.getStyle('height').toInt();
4549                 this.columnEl.setStyle('height', columnHeight);
4551                 if (this.options.sortable){
4552                         if (!this.options.container.retrieve('sortables')){
4553                                 var sortables = new Sortables(this.columnEl, {
4554                                         opacity: 1,
4555                                         handle: '.panel-header',
4556                                         constrain: false,
4557                                         revert: false,
4558                                         onSort: function(){
4559                                                 $$('.column').each(function(column){
4560                                                         column.getChildren('.panelWrapper').each(function(panelWrapper){
4561                                                                 panelWrapper.getElement('.panel').removeClass('bottomPanel');
4562                                                         });
4563                                                         if (column.getChildren('.panelWrapper').getLast()){
4564                                                                 column.getChildren('.panelWrapper').getLast().getElement('.panel').addClass('bottomPanel');
4565                                                         }
4566                                                         MUI.panelHeight();
4567                                                 }.bind(this));
4568                                         }.bind(this)
4569                                 });
4570                                 this.options.container.store('sortables', sortables);
4571                         }
4572                         else {
4573                                 this.options.container.retrieve('sortables').addLists(this.columnEl);
4574                         }
4575                 }
4577                 if (options.placement == 'main'){
4578                         this.columnEl.addClass('rWidth');
4579                 }
4581                 switch (this.options.placement) {
4582                         case 'left':
4583                                 this.handleEl = new Element('div', {
4584                                         'id': this.options.id + '_handle',
4585                                         'class': 'columnHandle'
4586                                 }).inject(this.columnEl, 'after');
4588                                 this.handleIconEl = new Element('div', {
4589                                         'id': options.id + '_handle_icon',
4590                                         'class': 'handleIcon'
4591                                 }).inject(this.handleEl);
4593                                 addResizeRight(this.columnEl, options.resizeLimit[0], options.resizeLimit[1]);
4594                                 break;
4595                         case 'right':
4596                                 this.handleEl = new Element('div', {
4597                                         'id': this.options.id + '_handle',
4598                                         'class': 'columnHandle'
4599                                 }).inject(this.columnEl, 'before');
4601                                 this.handleIconEl = new Element('div', {
4602                                         'id': options.id + '_handle_icon',
4603                                         'class': 'handleIcon'
4604                                 }).inject(this.handleEl);
4605                                 addResizeLeft(this.columnEl, options.resizeLimit[0], options.resizeLimit[1]);
4606                                 break;
4607                 }
4609                 if (this.handleEl != null){
4610                         this.handleEl.addEvent('dblclick', function(){
4611                                 this.columnToggle();
4612                         }.bind(this));
4613                 }
4615                 MUI.rWidth();
4617         },
4618         columnToggle: function(){
4619                 var column = this.columnEl;
4621                 // Collapse
4622                 if (this.isCollapsed == false){
4623                         this.oldWidth = column.getStyle('width').toInt();
4625                         this.resize.detach();
4626                         this.handleEl.removeEvents('dblclick');
4627                         this.handleEl.addEvent('click', function(){
4628                                 this.columnToggle();
4629                         }.bind(this));
4630                         this.handleEl.setStyle('cursor', 'pointer').addClass('detached');
4632                         column.setStyle('width', 0);
4633                         this.isCollapsed = true;
4634                         column.addClass('collapsed');
4635                         column.removeClass('expanded');
4636                         MUI.rWidth();
4637                         this.fireEvent('onCollapse');
4638                 }
4639                 // Expand
4640                 else {
4641                         column.setStyle('width', this.oldWidth);
4642                         this.isCollapsed = false;
4643                         column.addClass('expanded');
4644                         column.removeClass('collapsed');
4646                         this.handleEl.removeEvents('click');
4647                         this.handleEl.addEvent('dblclick', function(){
4648                                 this.columnToggle();
4649                         }.bind(this));
4650                         this.resize.attach();
4651                         this.handleEl.setStyle('cursor', (Browser.Engine.webkit || Browser.Engine.gecko) ? 'col-resize' : 'e-resize').addClass('attached');
4653                         MUI.rWidth();
4654                         this.fireEvent('onExpand');
4655                 }
4656         }
4658 MUI.Column.implement(new Options, new Events);
4662 Class: Panel
4663         Create a panel. Panels go one on top of another in columns. Create your columns first and then add your panels. Panels should be created from top to bottom, left to right.
4665 Syntax:
4666 (start code)
4667         MUI.Panel();
4668 (end)
4670 Arguments:
4671         options
4673 Options:
4674         id - The ID of the panel. This must be set when creating the panel.
4675         column - Where to inject the panel. This must be set when creating the panel.
4676         loadMethod - ('html', 'xhr', or 'iframe') Defaults to 'html' if there is no contentURL. Defaults to 'xhr' if there is a contentURL. You only really need to set this if using the 'iframe' method. May create a 'panel' loadMethod in the future.
4677         contentURL - Used if loadMethod is set to 'xhr' or 'iframe'.
4678         method - ('get', or 'post') The method used to get the data. Defaults to 'get'.
4679         data - (hash) Data to send with the URL. Defaults to null.
4680         evalScripts - (boolean) An xhr loadMethod option. Defaults to true.
4681         evalResponse - (boolean) An xhr loadMethod option. Defaults to false.
4682         content - (string or element) An html loadMethod option.
4683         tabsURL - (url)
4684         tabsData - (hash) Data to send with the URL. Defaults to null.
4685         tabsOnload - (function)
4686         header - (boolean) Display the panel header or not
4687         headerToolbox: (boolean)
4688         headerToolboxURL: (url)
4689         headerToolboxOnload: (function)
4690         height - (number) Height of content area.
4691         addClass - (string) Add a class to the panel.
4692         scrollbars - (boolean)
4693         padding - (object)
4694         collapsible - (boolean)
4695         onBeforeBuild - (function) Fired before the panel is created.
4696         onContentLoaded - (function) Fired after the panel's conten is loaded.
4697         onResize - (function) Fired when the panel is resized.
4698         onCollapse - (function) Fired when the panel is collapsed.
4699         onExpand - (function) Fired when the panel is expanded.
4702 MUI.Panel = new Class({
4704         Implements: [Events, Options],
4706         options: {
4707                 id:                 null,
4708                 title:              'New Panel',
4709                 column:             null,
4710                 require:            {
4711                         css:            [],
4712                         images:         [],
4713                         js:             [],
4714                         onload:         null
4715                 },
4716                 loadMethod:         null,
4717                 contentURL:         null,
4719                 // xhr options
4720                 method:             'get',
4721                 data:               null,
4722                 evalScripts:        true,
4723                 evalResponse:       false,
4725                 // html options
4726                 content:            'Panel content',
4728                 // Tabs
4729                 tabsURL:            null,
4730                 tabsData:           null,
4731                 tabsOnload:         $empty,
4733                 header:             true,
4734                 headerToolbox:      false,
4735                 headerToolboxURL:   'pages/lipsum.html',
4736                 headerToolboxOnload: $empty,
4738                 // Style options:
4739                 height:             125,
4740                 addClass:           '',
4741                 scrollbars:         true,
4742                 padding:                    { top: 8, right: 8, bottom: 8, left: 8 },
4744                 // Other:
4745                 collapsible:        true,
4747                 // Events
4748                 onBeforeBuild:       $empty,
4749                 onContentLoaded:     $empty,
4750                 onResize:            $empty,
4751                 onCollapse:          $empty,
4752                 onExpand:            $empty
4754         },
4755         initialize: function(options){
4756                 this.setOptions(options);
4758                 $extend(this, {
4759                         timestamp: $time(),
4760                         isCollapsed: false, // This is probably redundant since we can check for the class
4761                         oldHeight: 0,
4762                         partner: null
4763                 });
4765                 // If panel has no ID, give it one.
4766                 if (this.options.id == null){
4767                         this.options.id = 'panel' + (++MUI.Panels.panelIDCount);
4768                 }
4770                 // Shorten object chain
4771                 var instances = MUI.Panels.instances;
4772                 var instanceID = instances.get(this.options.id);
4773                 var options = this.options;
4775                 // Check to see if there is already a class instance for this panel
4776                 if (instanceID){
4777                         var instance = instanceID;
4778                 }
4780                 // Check if panel already exists
4781                 if ( this.panelEl ){
4782                         return;
4783                 }
4784                 else {
4785                         instances.set(this.options.id, this);
4786                 }
4788                 this.fireEvent('onBeforeBuild');
4790                 if (options.loadMethod == 'iframe') {
4791                         // Iframes have their own padding.
4792                         options.padding = { top: 0, right: 0, bottom: 0, left: 0 };
4793                 }
4795                 this.showHandle = true;
4796                 if ($(options.column).getChildren().length == 0) {
4797                         this.showHandle = false;
4798                 }
4800                 this.panelWrapperEl = new Element('div', {
4801                         'id': this.options.id + '_wrapper',
4802                         'class': 'panelWrapper expanded'
4803                 }).inject($(options.column));
4805                 this.panelEl = new Element('div', {
4806                         'id': this.options.id,
4807                         'class': 'panel expanded',
4808                         'styles': {
4809                                 'height': options.height
4810                         }
4811                 }).inject(this.panelWrapperEl);
4813                 this.panelEl.store('instance', this);
4815                 this.panelEl.addClass(options.addClass);
4817                 this.contentEl = new Element('div', {
4818                         'id': options.id + '_pad',
4819                         'class': 'pad'
4820                 }).inject(this.panelEl);
4822                 // This is in order to use the same variable as the windows do in updateContent.
4823                 // May rethink this.
4824                 this.contentWrapperEl = this.panelEl;
4826                 this.contentEl.setStyles({
4827                         'padding-top': options.padding.top,
4828                         'padding-bottom': options.padding.bottom,
4829                         'padding-left': options.padding.left,
4830                         'padding-right': options.padding.right
4831                 });
4833                 this.panelHeaderEl = new Element('div', {
4834                         'id': this.options.id + '_header',
4835                         'class': 'panel-header',
4836                         'styles': {
4837                                 'display': options.header ? 'block' : 'none'
4838                         }
4839                 }).inject(this.panelEl, 'before');
4841                 var columnInstances = MUI.Columns.instances;
4842                 var columnInstance = columnInstances.get(this.options.column);
4844                 if (columnInstance.options.sortable){
4845                         this.panelHeaderEl.setStyle('cursor', 'move');
4846                         columnInstance.options.container.retrieve('sortables').addItems(this.panelWrapperEl);
4847                 }
4849                 if (this.options.collapsible) {
4850                         this.collapseToggleInit();
4851                 }
4853                 if (this.options.headerToolbox) {
4854                         this.panelHeaderToolboxEl = new Element('div', {
4855                                 'id': options.id + '_headerToolbox',
4856                                 'class': 'panel-header-toolbox'
4857                         }).inject(this.panelHeaderEl);
4858                 }
4860                 this.panelHeaderContentEl = new Element('div', {
4861                         'id': options.id + '_headerContent',
4862                         'class': 'panel-headerContent'
4863                 }).inject(this.panelHeaderEl);
4865                 this.titleEl = new Element('h2', {
4866                         'id': options.id + '_title'
4867                 }).inject(this.panelHeaderContentEl);
4869                 this.handleEl = new Element('div', {
4870                         'id': options.id + '_handle',
4871                         'class': 'horizontalHandle',
4872                         'styles': {
4873                                 'display': this.showHandle == true ? 'block' : 'none'
4874                         }
4875                 }).inject(this.panelEl, 'after');
4877                 this.handleIconEl = new Element('div', {
4878                         'id': options.id + '_handle_icon',
4879                         'class': 'handleIcon'
4880                 }).inject(this.handleEl);
4882                 addResizeBottom(options.id);
4884                 if (options.require.css.length || options.require.images.length){
4885                         new MUI.Require({
4886                                 css: options.require.css,
4887                                 images: options.require.images,
4888                                 onload: function(){
4889                                         this.newPanel();
4890                                 }.bind(this)
4891                         });
4892                 }
4893                 else {
4894                         this.newPanel();
4895                 }
4896         },
4897         newPanel: function(){
4899                 options = this.options;
4901                 if (this.options.headerToolbox) {
4902                         MUI.updateContent({
4903                                 'element': this.panelEl,
4904                                 'childElement': this.panelHeaderToolboxEl,
4905                                 'loadMethod': 'xhr',
4906                                 'url': options.headerToolboxURL,
4907                                 'onContentLoaded': options.headerToolboxOnload
4908                         });
4909                 }
4911                 if (options.tabsURL == null) {
4912                         this.titleEl.set('html', options.title);
4913                 } else {
4914                         this.panelHeaderContentEl.addClass('tabs');
4915                         MUI.updateContent({
4916                                 'element': this.panelEl,
4917                                 'childElement': this.panelHeaderContentEl,
4918                                 'loadMethod': 'xhr',
4919                                 'url': options.tabsURL,
4920                                 'data': options.tabsData,
4921                                 'onContentLoaded': options.tabsOnload
4922                         });
4923                 }
4925                 // Add content to panel.
4926                 MUI.updateContent({
4927                         'element': this.panelEl,
4928                         'content': options.content,
4929                         'method': options.method,
4930                         'data': options.data,
4931                         'url': options.contentURL,
4932                         'onContentLoaded': null,
4933                         'require': {
4934                                 js: options.require.js,
4935                                 onload: options.require.onload
4936                         }
4937                 });
4939                 // Do this when creating and removing panels
4940                 $(options.column).getChildren('.panelWrapper').each(function(panelWrapper){
4941                         panelWrapper.getElement('.panel').removeClass('bottomPanel');
4942                 });
4943                 $(options.column).getChildren('.panelWrapper').getLast().getElement('.panel').addClass('bottomPanel');
4945                 MUI.panelHeight(options.column, this.panelEl, 'new');
4947         },
4948         collapseToggleInit: function(options){
4950                 var options = this.options;
4952                 this.panelHeaderCollapseBoxEl = new Element('div', {
4953                         'id': options.id + '_headerCollapseBox',
4954                         'class': 'toolbox'
4955                 }).inject(this.panelHeaderEl);
4957                 if (options.headerToolbox) {
4958                         this.panelHeaderCollapseBoxEl.addClass('divider');
4959                 }
4961                 this.collapseToggleEl = new Element('div', {
4962                         'id': options.id + '_collapseToggle',
4963                         'class': 'panel-collapse icon16',
4964                         'styles': {
4965                                 'width': 16,
4966                                 'height': 16
4967                         },
4968                         'title': 'Collapse Panel'
4969                 }).inject(this.panelHeaderCollapseBoxEl);
4971                 this.collapseToggleEl.addEvent('click', function(event){
4972                         var panel = this.panelEl;
4973                         var panelWrapper = this.panelWrapperEl
4975                         // Get siblings and make sure they are not all collapsed.
4976                         // If they are all collapsed and the current panel is collapsing
4977                         // Then collapse the column.
4978                         var instances = MUI.Panels.instances;
4979                         var expandedSiblings = [];
4981                         panelWrapper.getAllPrevious('.panelWrapper').each(function(sibling){
4982                                 var instance = instances.get(sibling.getElement('.panel').id);
4983                                 if (instance.isCollapsed == false){
4984                                         expandedSiblings.push(sibling.getElement('.panel').id);
4985                                 }
4986                         });
4988                         panelWrapper.getAllNext('.panelWrapper').each(function(sibling){
4989                                 var instance = instances.get(sibling.getElement('.panel').id);
4990                                 if (instance.isCollapsed == false){
4991                                         expandedSiblings.push(sibling.getElement('.panel').id);
4992                                 }
4993                         });
4995                         // Collapse Panel
4996                         if (this.isCollapsed == false) {
4997                                 var currentColumn = MUI.Columns.instances.get($(options.column).id);
4999                                 if (expandedSiblings.length == 0 && currentColumn.options.placement != 'main'){
5000                                         var currentColumn = MUI.Columns.instances.get($(options.column).id);
5001                                         currentColumn.columnToggle();
5002                                         return;
5003                                 }
5004                                 else if (expandedSiblings.length == 0 && currentColumn.options.placement == 'main'){
5005                                         return;
5006                                 }
5007                                 this.oldHeight = panel.getStyle('height').toInt();
5008                                 if (this.oldHeight < 10) this.oldHeight = 20;
5009                                 this.contentEl.setStyle('position', 'absolute'); // This is so IE6 and IE7 will collapse the panel all the way
5010                                 panel.setStyle('height', 0);
5011                                 this.isCollapsed = true;
5012                                 panelWrapper.addClass('collapsed');
5013                                 panelWrapper.removeClass('expanded');
5014                                 MUI.panelHeight(options.column, panel, 'collapsing');
5015                                 MUI.panelHeight(); // Run this a second time for panels within panels
5016                                 this.collapseToggleEl.removeClass('panel-collapsed');
5017                                 this.collapseToggleEl.addClass('panel-expand');
5018                                 this.collapseToggleEl.setProperty('title','Expand Panel');
5019                                 this.fireEvent('onCollapse');
5020                         }
5022                         // Expand Panel
5023                         else {
5024                                 this.contentEl.setStyle('position', null); // This is so IE6 and IE7 will collapse the panel all the way
5025                                 panel.setStyle('height', this.oldHeight);
5026                                 this.isCollapsed = false;
5027                                 panelWrapper.addClass('expanded');
5028                                 panelWrapper.removeClass('collapsed');
5029                                 MUI.panelHeight(this.options.column, panel, 'expanding');
5030                                 MUI.panelHeight(); // Run this a second time for panels within panels
5031                                 this.collapseToggleEl.removeClass('panel-expand');
5032                                 this.collapseToggleEl.addClass('panel-collapsed');
5033                                 this.collapseToggleEl.setProperty('title','Collapse Panel');
5034                                 this.fireEvent('onExpand');
5035                         }
5036                 }.bind(this));
5037         }
5039 MUI.Panel.implement(new Options, new Events);
5042         arguments:
5043                 column - The column to resize the panels in
5044                 changing -  The panel that is collapsing, expanding, or new
5045                 action - collapsing, expanding, or new
5049 MUI.extend({
5050         // Panel Height
5051         panelHeight: function(column, changing, action){
5052                 if (column != null) {
5053                         MUI.panelHeight2($(column), changing, action);
5054                 }
5055                 else {
5056                         $$('.column').each(function(column){
5057                                 MUI.panelHeight2(column);
5058                         }.bind(this));
5059                 }
5060         },
5061         /*
5063         actions can be new, collapsing or expanding.
5065         */
5066         panelHeight2: function(column, changing, action){
5068                 var instances = MUI.Panels.instances;
5070                 var parent = column.getParent();
5071                 var columnHeight = parent.getStyle('height').toInt();
5072                 if (Browser.Engine.trident4 && parent == MUI.Desktop.pageWrapper) {
5073                         columnHeight -= 1;
5074                 }
5075                 column.setStyle('height', columnHeight);
5077                 // Get column panels
5078                 var panels = [];
5079                 column.getChildren('.panelWrapper').each( function(panelWrapper){
5080                         panels.push(panelWrapper.getElement('.panel'));
5081                 }.bind(this));
5083                 // Get expanded column panels
5084                 var panelsExpanded = [];
5085                 column.getChildren('.expanded').each( function(panelWrapper){
5086                         panelsExpanded.push(panelWrapper.getElement('.panel'));
5087                 }.bind(this));
5089                  // All the panels in the column whose height will be effected.
5090                 var panelsToResize = [];
5092                 // The panel with the greatest height. Remainders will be added to this panel
5093                 var tallestPanel;
5094                 var tallestPanelHeight = 0;
5096                 this.panelsTotalHeight = 0; // Height of all the panels in the column
5097                 this.height = 0; // Height of all the elements in the column
5099                 // Set panel resize partners
5100                 panels.each(function(panel){
5101                         instance = instances.get(panel.id);
5102                         if (panel.getParent().hasClass('expanded') && panel.getParent().getNext('.expanded')) {
5103                                 instance.partner = panel.getParent().getNext('.expanded').getElement('.panel');
5104                                 instance.resize.attach();
5105                                 instance.handleEl.setStyles({
5106                                         'display': 'block',
5107                                         'cursor': (Browser.Engine.webkit || Browser.Engine.gecko) ? 'row-resize' : 'n-resize'
5108                                 }).removeClass('detached');
5109                         } else {
5110                                 instance.resize.detach();
5111                                 instance.handleEl.setStyles({
5112                                         'display': 'none',
5113                                         'cursor': null
5114                                 }).addClass('detached');
5115                         }
5116                         if (panel.getParent().getNext('.panelWrapper') == null) {
5117                                 instance.handleEl.hide();
5118                         }
5119                 }.bind(this));
5121                 // Add panels to panelsToResize
5122                 // Get the total height of all the resizable panels
5123                 // Get the total height of all the column's children
5124                 column.getChildren().each(function(panelWrapper){
5126                 panelWrapper.getChildren().each(function(el){
5128                         if (el.hasClass('panel')){
5129                                 var instance = instances.get(el.id);
5131                                 // Are any next siblings Expanded?
5132                                 anyNextSiblingsExpanded = function(el){
5133                                         var test;
5134                                         el.getParent().getAllNext('.panelWrapper').each(function(sibling){
5135                                                 var siblingInstance = instances.get(sibling.getElement('.panel').id);
5136                                                 if (siblingInstance.isCollapsed == false){
5137                                                         test = true;
5138                                                 }
5139                                         }.bind(this));
5140                                         return test;
5141                                 }.bind(this);
5143                                 // If a next sibling is expanding, are any of the nexts siblings of the expanding sibling Expanded?
5144                                 anyExpandingNextSiblingsExpanded = function(el){
5145                                         var test;
5146                                         changing.getParent().getAllNext('.panelWrapper').each(function(sibling){
5147                                                 var siblingInstance = instances.get(sibling.getElement('.panel').id);
5148                                                 if (siblingInstance.isCollapsed == false){
5149                                                         test = true;
5150                                                 }
5151                                         }.bind(this));
5152                                         return test;
5153                                 }.bind(this);
5155                                 // Is the panel that is collapsing, expanding, or new located after this panel?
5156                                 anyNextContainsChanging = function(el){
5157                                         var allNext = [];
5158                                         el.getParent().getAllNext('.panelWrapper').each(function(panelWrapper){
5159                                                 allNext.push(panelWrapper.getElement('.panel'));
5160                                         }.bind(this));
5161                                         var test = allNext.contains(changing);
5162                                         return test;
5163                                 }.bind(this);
5165                                 nextExpandedChanging = function(el){
5166                                         var test;
5167                                         if (el.getParent().getNext('.expanded')){
5168                                                 if (el.getParent().getNext('.expanded').getElement('.panel') == changing) test = true;
5169                                         }
5170                                         return test;
5171                                 }
5173                                 // NEW PANEL
5174                                 // Resize panels that are "new" or not collapsed
5175                                 if (action == 'new') {
5176                                         if (!instance.isCollapsed && el != changing) {
5177                                                 panelsToResize.push(el);
5178                                                 this.panelsTotalHeight += el.offsetHeight.toInt();
5179                                         }
5180                                 }
5182                                 // COLLAPSING PANELS and CURRENTLY EXPANDED PANELS
5183                                 // Resize panels that are not collapsed.
5184                                 // If a panel is collapsing resize any expanded panels below.
5185                                 // If there are no expanded panels below it, resize the expanded panels above it.
5186                                 else if (action == null || action == 'collapsing' ){
5187                                         if (!instance.isCollapsed && (!anyNextContainsChanging(el) || !anyNextSiblingsExpanded(el))){
5188                                                 panelsToResize.push(el);
5189                                                 this.panelsTotalHeight += el.offsetHeight.toInt();
5190                                         }
5191                                 }
5193                                 // EXPANDING PANEL
5194                                 // Resize panels that are not collapsed and are not expanding.
5195                                 // Resize any expanded panels below the expanding panel.
5196                                 // If there are no expanded panels below the expanding panel, resize the first expanded panel above it.
5197                                 else if (action == 'expanding' && !instance.isCollapsed  && el != changing){
5198                                         if (!anyNextContainsChanging(el) || (!anyExpandingNextSiblingsExpanded(el) && nextExpandedChanging(el))){
5199                                                 panelsToResize.push(el);
5200                                                 this.panelsTotalHeight += el.offsetHeight.toInt();
5201                                         }
5202                                 }
5204                                 if (el.style.height){
5205                                         this.height += el.getStyle('height').toInt();
5206                                 }
5207                         }
5208                         else {
5209                                 this.height += el.offsetHeight.toInt();
5210                         }
5211                 }.bind(this));
5213                 }.bind(this));
5215                 // Get the remaining height
5216                 var remainingHeight = column.offsetHeight.toInt() - this.height;
5218                 this.height = 0;
5220                 // Get height of all the column's children
5221                 column.getChildren().each(function(el){
5222                         this.height += el.offsetHeight.toInt();
5223                 }.bind(this));
5225                 var remainingHeight = column.offsetHeight.toInt() - this.height;
5227                 panelsToResize.each(function(panel){
5228                         var ratio = this.panelsTotalHeight / panel.offsetHeight.toInt();
5229                         var panelHeight = panel.getStyle('height').toInt();
5230                         var newPanelHeight = remainingHeight / ratio;
5231                         if (!isNaN(panelHeight))
5232                                 newPanelHeight += panelHeight;
5233                         if (newPanelHeight < 1){
5234                                 newPanelHeight = 0;
5235                         }
5236                         panel.setStyle('height', newPanelHeight);
5237                 }.bind(this));
5239                 // Make sure the remaining height is 0. If not add/subtract the
5240                 // remaining height to the tallest panel. This makes up for browser resizing,
5241                 // off ratios, and users trying to give panels too much height.
5243                 // Get height of all the column's children
5244                 this.height = 0;
5245                 column.getChildren().each(function(panelWrapper){
5246                         panelWrapper.getChildren().each(function(el){
5247                                 this.height += el.offsetHeight.toInt();
5248                                 if (el.hasClass('panel') && el.getStyle('height').toInt() > tallestPanelHeight){
5249                                         tallestPanel = el;
5250                                         tallestPanelHeight = el.getStyle('height').toInt();
5251                                 }
5252                         }.bind(this));
5253                 }.bind(this));
5255                 var remainingHeight = column.offsetHeight.toInt() - this.height;
5257                 if (remainingHeight != 0 && tallestPanelHeight > 0){
5258                         tallestPanel.setStyle('height', tallestPanel.getStyle('height').toInt() + remainingHeight );
5259                         if (tallestPanel.getStyle('height') < 1){
5260                                 tallestPanel.setStyle('height', 0 );
5261                         }
5262                 }
5264                 parent.getChildren('.columnHandle').each(function(handle){
5265                         var parent = handle.getParent();
5266                         if (parent.getStyle('height').toInt() < 1) return; // Keeps IE7 and 8 from throwing an error when collapsing a panel within a panel
5267                         var handleHeight = parent.getStyle('height').toInt() - handle.getStyle('margin-top').toInt() - handle.getStyle('margin-bottom').toInt();
5268                         if (Browser.Engine.trident4 && parent == MUI.Desktop.pageWrapper){
5269                                 handleHeight -= 1;
5270                         }
5271                         handle.setStyle('height', handleHeight);
5272                 });
5274                 panelsExpanded.each(function(panel){
5275                         MUI.resizeChildren(panel);
5276                 }.bind(this));
5278         },
5279         // May rename this resizeIframeEl()
5280         resizeChildren: function(panel){
5281                 var instances = MUI.Panels.instances;
5282                 var instance = instances.get(panel.id);
5283                 var contentWrapperEl = instance.contentWrapperEl;
5285                 if (instance.iframeEl) {
5286                         if (!MUI.ieLegacySupport) {
5287                                 instance.iframeEl.setStyles({
5288                                         'height': contentWrapperEl.getStyle('height'),
5289                                         'width': contentWrapperEl.offsetWidth - contentWrapperEl.getStyle('margin-left').toInt() - contentWrapperEl.getStyle('margin-right').toInt()
5290                                 });
5291                         }
5292                         else {
5293                                 // The following hack is to get IE8 RC1 IE8 Standards Mode to properly resize an iframe
5294                                 // when only the vertical dimension is changed.
5295                                 instance.iframeEl.setStyles({
5296                                         'height': contentWrapperEl.getStyle('height'),
5297                                         'width': contentWrapperEl.offsetWidth - contentWrapperEl.getStyle('margin-left').toInt() - contentWrapperEl.getStyle('margin-right').toInt() - 1
5298                                 });
5299                                 instance.iframeEl.setStyles({
5300                                         'width': contentWrapperEl.offsetWidth - contentWrapperEl.getStyle('margin-left').toInt() - contentWrapperEl.getStyle('margin-right').toInt()
5301                                 });
5302                         }
5303                 }
5305         },
5306         // Remaining Width
5307         rWidth: function(container){
5308                 if (container == null) {
5309                         var container = MUI.Desktop.desktop;
5310                 }
5311                 container.getElements('.rWidth').each(function(column){
5312                         var currentWidth = column.offsetWidth.toInt();
5313                         currentWidth -= column.getStyle('margin-left').toInt();
5314                         currentWidth -= column.getStyle('margin-right').toInt();
5315                         var parent = column.getParent();
5316                         this.width = 0;
5318                         // Get the total width of all the parent element's children
5319                         parent.getChildren().each(function(el){
5320                                 if (el.hasClass('mocha') != true) {
5321                                         this.width += el.offsetWidth.toInt();
5322                                 }
5323                         }.bind(this));
5325                         // Add the remaining width to the current element
5326                         var remainingWidth = parent.offsetWidth.toInt() - this.width;
5327                         var newWidth = currentWidth + remainingWidth;
5328                         if (newWidth < 1) newWidth = 0;
5329                         column.setStyle('width', newWidth);
5330                         column.getChildren('.panel').each(function(panel){
5331                                 panel.setStyle('width', newWidth - panel.getStyle('margin-left').toInt() - panel.getStyle('margin-right').toInt());
5332                                 MUI.resizeChildren(panel);
5333                         }.bind(this));
5335                 });
5336         }
5340 function addResizeRight(element, min, max){
5341         if (!$(element)) return;
5342         element = $(element);
5344         var instances = MUI.Columns.instances;
5345         var instance = instances.get(element.id);
5347         var handle = element.getNext('.columnHandle');
5348         handle.setStyle('cursor', (Browser.Engine.webkit || Browser.Engine.gecko) ? 'col-resize' : 'e-resize');
5349         if (!min) min = 50;
5350         if (!max) max = 250;
5351         if (MUI.ieLegacySupport) {
5352                 handle.addEvents({
5353                         'mousedown': function(){
5354                                 handle.setCapture();
5355                         },
5356                         'mouseup': function(){
5357                                 handle.releaseCapture();
5358                         }
5359                 });
5360         }
5361         instance.resize = element.makeResizable({
5362                 handle: handle,
5363                 modifiers: {
5364                         x: 'width',
5365                         y: false
5366                 },
5367                 limit: {
5368                         x: [min, max]
5369                 },
5370                 onStart: function(){
5371                         element.getElements('iframe').setStyle('visibility', 'hidden');
5372                         element.getNext('.column').getElements('iframe').setStyle('visibility', 'hidden');
5373                 }.bind(this),
5374                 onDrag: function(){
5375                         MUI.rWidth(element.getParent());
5376                         if (Browser.Engine.trident4) {
5377                                 element.getChildren().each(function(el){
5378                                         var width = $(element).getStyle('width').toInt();
5379                                         width -= el.getStyle('margin-right').toInt();
5380                                         width -= el.getStyle('margin-left').toInt();
5381                                         width -= el.getStyle('padding-right').toInt();
5382                                         width -= el.getStyle('padding-left').toInt();
5383                                         el.setStyle('width', width);
5384                                 }.bind(this));
5385                         }
5386                 }.bind(this),
5387                 onComplete: function(){
5388                         MUI.rWidth(element.getParent());
5389                         element.getElements('iframe').setStyle('visibility', 'visible');
5390                         element.getNext('.column').getElements('iframe').setStyle('visibility', 'visible');
5391                         instance.fireEvent('onResize');
5392                 }.bind(this)
5393         });
5396 function addResizeLeft(element, min, max){
5397         if (!$(element)) return;
5398         element = $(element);
5400         var instances = MUI.Columns.instances;
5401         var instance = instances.get(element.id);
5403         var handle = element.getPrevious('.columnHandle');
5404         handle.setStyle('cursor', (Browser.Engine.webkit || Browser.Engine.gecko) ? 'col-resize' : 'e-resize');
5405         var partner = element.getPrevious('.column');
5406         if (!min) min = 50;
5407         if (!max) max = 250;
5408         if (MUI.ieLegacySupport){
5409                 handle.addEvents({
5410                         'mousedown': function(){
5411                                 handle.setCapture();
5412                         },
5413                         'mouseup': function(){
5414                                 handle.releaseCapture();
5415                         }
5416                 });
5417         }
5418         instance.resize = element.makeResizable({
5419                 handle: handle,
5420                 modifiers: {x: 'width' , y: false},
5421                 invert: true,
5422                 limit: { x: [min, max] },
5423                 onStart: function(){
5424                         $(element).getElements('iframe').setStyle('visibility','hidden');
5425                         partner.getElements('iframe').setStyle('visibility','hidden');
5426                 }.bind(this),
5427                 onDrag: function(){
5428                         MUI.rWidth(element.getParent());
5429                 }.bind(this),
5430                 onComplete: function(){
5431                         MUI.rWidth(element.getParent());
5432                         $(element).getElements('iframe').setStyle('visibility','visible');
5433                         partner.getElements('iframe').setStyle('visibility','visible');
5434                         instance.fireEvent('onResize');
5435                 }.bind(this)
5436         });
5439 function addResizeBottom(element){
5440         if (!$(element)) return;
5441         var element = $(element);
5443         var instances = MUI.Panels.instances;
5444         var instance = instances.get(element.id);
5445         var handle = instance.handleEl;
5446         handle.setStyle('cursor', (Browser.Engine.webkit || Browser.Engine.gecko) ? 'row-resize' : 'n-resize');
5447         partner = instance.partner;
5448         min = 0;
5449         max = function(){
5450                 return element.getStyle('height').toInt() + partner.getStyle('height').toInt();
5451         }.bind(this);
5453         if (MUI.ieLegacySupport) {
5454                 handle.addEvents({
5455                         'mousedown': function(){
5456                                 handle.setCapture();
5457                         },
5458                         'mouseup': function(){
5459                                 handle.releaseCapture();
5460                         }
5461                 });
5462         }
5463         instance.resize = element.makeResizable({
5464                 handle: handle,
5465                 modifiers: {x: false, y: 'height'},
5466                 limit: { y: [min, max] },
5467                 invert: false,
5468                 onBeforeStart: function(){
5469                         partner = instance.partner;
5470                         this.originalHeight = element.getStyle('height').toInt();
5471                         this.partnerOriginalHeight = partner.getStyle('height').toInt();
5472                 }.bind(this),
5473                 onStart: function(){
5474                         if (instance.iframeEl) {
5475                                 if (!MUI.ieLegacySupport) {
5476                                         instance.iframeEl.setStyle('visibility', 'hidden');
5477                                         partner.getElements('iframe').setStyle('visibility','hidden');
5478                                 }
5479                                 else {
5480                                         instance.iframeEl.hide();
5481                                         partner.getElements('iframe').hide();
5482                                 }
5483                         }
5485                 }.bind(this),
5486                 onDrag: function(){
5487                         partnerHeight = partnerOriginalHeight;
5488                         partnerHeight += (this.originalHeight - element.getStyle('height').toInt());
5489                         partner.setStyle('height', partnerHeight);
5490                         MUI.resizeChildren(element, element.getStyle('height').toInt());
5491                         MUI.resizeChildren(partner, partnerHeight);
5492                         element.getChildren('.column').each( function(column){
5493                                 MUI.panelHeight(column);
5494                         });
5495                         partner.getChildren('.column').each( function(column){
5496                                 MUI.panelHeight(column);
5497                         });
5498                 }.bind(this),
5499                 onComplete: function(){
5500                         partnerHeight = partnerOriginalHeight;
5501                         partnerHeight += (this.originalHeight - element.getStyle('height').toInt());
5502                         partner.setStyle('height', partnerHeight);
5503                         MUI.resizeChildren(element, element.getStyle('height').toInt());
5504                         MUI.resizeChildren(partner, partnerHeight);
5505                         element.getChildren('.column').each( function(column){
5506                                 MUI.panelHeight(column);
5507                         });
5508                         partner.getChildren('.column').each( function(column){
5509                                 MUI.panelHeight(column);
5510                         });
5511                         if (instance.iframeEl) {
5512                                 if (!MUI.ieLegacySupport) {
5513                                         instance.iframeEl.setStyle('visibility', 'visible');
5514                                         partner.getElements('iframe').setStyle('visibility','visible');
5515                                 }
5516                                 else {
5517                                         instance.iframeEl.show();
5518                                         partner.getElements('iframe').show();
5519                                         // The following hack is to get IE8 Standards Mode to properly resize an iframe
5520                                         // when only the vertical dimension is changed.
5521                                         var width = instance.iframeEl.getStyle('width').toInt();
5522                                         instance.iframeEl.setStyle('width', width - 1);
5523                                         MUI.rWidth();
5524                                         instance.iframeEl.setStyle('width', width);
5525                                 }
5526                         }
5527                         instance.fireEvent('onResize');
5528                 }.bind(this)
5529         });
5532 MUI.extend({
5533         /*
5535         Function: closeColumn
5536                 Destroys/removes a column.
5538         Syntax:
5539         (start code)
5540                 MUI.closeColumn();
5541         (end)
5543         Arguments:
5544                 columnEl - the ID of the column to be closed
5546         Returns:
5547                 true - the column was closed
5548                 false - the column was not closed
5550         */
5551         closeColumn: function(columnEl){
5552                 var instances = MUI.Columns.instances;
5553                 var instance = instances.get(columnEl.id);
5554                 if (columnEl != $(columnEl) || instance.isClosing) return;
5556                 instance.isClosing = true;
5558                 if (instance.options.sortable){
5559                         instance.container.retrieve('sortables').removeLists(this.columnEl);
5560                 }
5562                 // Destroy all the panels in the column.
5563                 var panels = columnEl.getChildren('.panel');
5564                 panels.each(function(panel){
5565                         MUI.closePanel($(panel.id));
5566                 }.bind(this));
5568                 if (MUI.ieLegacySupport) {
5569                         columnEl.dispose();
5570                         if (instance.handleEl != null) {
5571                                 instance.handleEl.dispose();
5572                         }
5573                 }
5574                 else {
5575                         columnEl.destroy();
5576                         if (instance.handleEl != null) {
5577                                 instance.handleEl.destroy();
5578                         }
5579                 }
5580                 if (MUI.Desktop) {
5581                         MUI.Desktop.resizePanels();
5582                 }
5583                 instances.erase(instance.options.id);
5584                 return true;
5585         },
5586         /*
5588         Function: closePanel
5589                 Destroys/removes a panel.
5591         Syntax:
5592         (start code)
5593                 MUI.closePanel();
5594         (end)
5596         Arguments:
5597                 panelEl - the ID of the panel to be closed
5599         Returns:
5600                 true - the panel was closed
5601                 false - the panel was not closed
5603         */
5604         closePanel: function(panelEl){
5605                 var instances = MUI.Panels.instances;
5606                 var instance = instances.get(panelEl.id);
5607                 if (panelEl != $(panelEl) || instance.isClosing) return;
5609                 var column = instance.options.column;
5611                 instance.isClosing = true;
5613                 var columnInstances = MUI.Columns.instances;
5614                 var columnInstance = columnInstances.get(column);
5616                 if (columnInstance.options.sortable){
5617                         columnInstance.options.container.retrieve('sortables').removeItems(instance.panelWrapperEl);
5618                 }
5620                 instance.panelWrapperEl.destroy();
5622                 if (MUI.Desktop) {
5623                         MUI.Desktop.resizePanels();
5624                 }
5626                 // Do this when creating and removing panels
5627                 $(column).getChildren('.panelWrapper').each(function(panelWrapper){
5628                         panelWrapper.getElement('.panel').removeClass('bottomPanel');
5629                 });
5630                 $(column).getChildren('.panelWrapper').getLast().getElement('.panel').addClass('bottomPanel');
5632                 instances.erase(instance.options.id);
5633                 return true;
5635         }
5639 Script: Dock.js
5640         Implements the dock/taskbar. Enables window minimize.
5642 Copyright:
5643         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
5645 License:
5646         MIT-style license.
5648 Requires:
5649         Core.js, Window.js, Layout.js
5651 Todo:
5652         - Make it so the dock requires no initial html markup.
5656 MUI.files[MUI.path.source + 'Layout/Dock.js'] = 'loaded';
5658 MUI.options.extend({
5659         // Naming options:
5660         // If you change the IDs of the Mocha Desktop containers in your HTML, you need to change them here as well.
5661         dockWrapper: 'dockWrapper',
5662         dock:        'dock'
5665 MUI.extend({
5666         /*
5668         Function: minimizeAll
5669                 Minimize all windows that are minimizable.
5671         */
5672         minimizeAll: function() {
5673                 $$('.mocha').each(function(windowEl){
5674                         var instance = windowEl.retrieve('instance');
5675                         if (!instance.isMinimized && instance.options.minimizable == true){
5676                                 MUI.Dock.minimizeWindow(windowEl);
5677                         }
5678                 }.bind(this));
5679         }
5682 MUI.Dock = {
5684         options: {
5685                 useControls:          true,      // Toggles autohide and dock placement controls.
5686                 dockPosition:         'bottom',  // Position the dock starts in, top or bottom.
5687                 // Style options
5688                 trueButtonColor:      [70, 245, 70],     // Color for autohide on
5689                 enabledButtonColor:   [115, 153, 191],
5690                 disabledButtonColor:  [170, 170, 170]
5691         },
5693         initialize: function(options){
5694                 // Stops if MUI.Desktop is not implemented
5695                 if (!MUI.Desktop) return;
5697                 MUI.dockVisible = true;
5698                 this.dockWrapper   = $(MUI.options.dockWrapper);
5699                 this.dock          = $(MUI.options.dock);
5700                 this.autoHideEvent = null;
5701                 this.dockAutoHide  = false;  // True when dock autohide is set to on, false if set to off
5703                 if (!this.dockWrapper) return;
5705                 if (!this.options.useControls){
5706                         if($('dockPlacement')){
5707                                 $('dockPlacement').setStyle('cursor', 'default');
5708                         }
5709                         if($('dockAutoHide')){
5710                                 $('dockAutoHide').setStyle('cursor', 'default');
5711                         }
5712                 }
5714                 this.dockWrapper.setStyles({
5715                         'display':  'block',
5716                         'position': 'absolute',
5717                         'top':      null,
5718                         'bottom':   MUI.Desktop.desktopFooter ? MUI.Desktop.desktopFooter.offsetHeight : 0,
5719                         'left':     0
5720                 });
5722                 if (this.options.useControls){
5723                         this.initializeDockControls();
5724                 }
5726                 // Add check mark to menu if link exists in menu
5727                 if ($('dockLinkCheck')){
5728                         this.sidebarCheck = new Element('div', {
5729                                 'class': 'check',
5730                                 'id': 'dock_check'
5731                         }).inject($('dockLinkCheck'));
5732                 }
5734                 this.dockSortables = new Sortables('#dockSort', {
5735                         opacity: 1,
5736                         constrain: true,
5737                         clone: false,
5738                         revert: false
5739                 });
5741                 MUI.Desktop.setDesktopSize();
5743                 if (MUI.myChain){
5744                         MUI.myChain.callChain();
5745                 }
5747         },
5749         initializeDockControls: function(){
5751                 // Convert CSS colors to Canvas colors.
5752                 this.setDockColors();
5754                 if (this.options.useControls){
5755                         // Insert canvas
5756                         var canvas = new Element('canvas', {
5757                                 'id':     'dockCanvas',
5758                                 'width':  '15',
5759                                 'height': '18'
5760                         }).inject(this.dock);
5762                         // Dynamically initialize canvas using excanvas. This is only required by IE
5763                         if (MUI.ieLegacySupport && MUI.ieSupport == 'excanvas'){
5764                                 G_vmlCanvasManager.initElement(canvas);
5765                         }
5766                 }
5768                 var dockPlacement = $('dockPlacement');
5769                 var dockAutoHide = $('dockAutoHide');
5771                 // Position top or bottom selector
5772                 dockPlacement.setProperty('title','Position Dock Top');
5774                 // Attach event
5775                 dockPlacement.addEvent('click', function(){
5776                         this.moveDock();
5777                 }.bind(this));
5779                 // Auto Hide toggle switch
5780                 dockAutoHide.setProperty('title','Turn Auto Hide On');
5782                 // Attach event Auto Hide
5783                 dockAutoHide.addEvent('click', function(event){
5784                         if ( this.dockWrapper.getProperty('dockPosition') == 'top' )
5785                                 return false;
5787                         var ctx = $('dockCanvas').getContext('2d');
5788                         this.dockAutoHide = !this.dockAutoHide; // Toggle
5789                         if (this.dockAutoHide){
5790                                 $('dockAutoHide').setProperty('title', 'Turn Auto Hide Off');
5791                                 //ctx.clearRect(0, 11, 100, 100);
5792                                 MUI.circle(ctx, 5 , 14, 3, this.options.trueButtonColor, 1.0);
5794                                 // Define event
5795                                 this.autoHideEvent = function(event) {
5796                                         if (!this.dockAutoHide)
5797                                                 return;
5798                                         if (!MUI.Desktop.desktopFooter) {
5799                                                 var dockHotspotHeight = this.dockWrapper.offsetHeight;
5800                                                 if (dockHotspotHeight < 25) dockHotspotHeight = 25;
5801                                         }
5802                                         else if (MUI.Desktop.desktopFooter) {
5803                                                 var dockHotspotHeight = this.dockWrapper.offsetHeight + MUI.Desktop.desktopFooter.offsetHeight;
5804                                                 if (dockHotspotHeight < 25) dockHotspotHeight = 25;
5805                                         }
5806                                         if (!MUI.Desktop.desktopFooter && event.client.y > (document.getCoordinates().height - dockHotspotHeight)){
5807                                                 if (!MUI.dockVisible){
5808                                                         this.dockWrapper.show();
5809                                                         MUI.dockVisible = true;
5810                                                         MUI.Desktop.setDesktopSize();
5811                                                 }
5812                                         }
5813                                         else if (MUI.Desktop.desktopFooter && event.client.y > (document.getCoordinates().height - dockHotspotHeight)){
5814                                                 if (!MUI.dockVisible){
5815                                                         this.dockWrapper.show();
5816                                                         MUI.dockVisible = true;
5817                                                         MUI.Desktop.setDesktopSize();
5818                                                 }
5819                                         }
5820                                         else if (MUI.dockVisible){
5821                                                 this.dockWrapper.hide();
5822                                                 MUI.dockVisible = false;
5823                                                 MUI.Desktop.setDesktopSize();
5825                                         }
5826                                 }.bind(this);
5828                                 // Add event
5829                                 document.addEvent('mousemove', this.autoHideEvent);
5831                         } else {
5832                                 $('dockAutoHide').setProperty('title', 'Turn Auto Hide On');
5833                                 //ctx.clearRect(0, 11, 100, 100);
5834                                 MUI.circle(ctx, 5 , 14, 3, this.options.enabledButtonColor, 1.0);
5835                                 // Remove event
5836                                 document.removeEvent('mousemove', this.autoHideEvent);
5837                         }
5839                 }.bind(this));
5841                 this.renderDockControls();
5843                 if (this.options.dockPosition == 'top'){
5844                         this.moveDock();
5845                 }
5847         },
5849         setDockColors: function(){
5850                 var dockButtonEnabled = MUI.getCSSRule('.dockButtonEnabled');
5851                 if (dockButtonEnabled && dockButtonEnabled.style.backgroundColor){
5852                         this.options.enabledButtonColor = new Color(dockButtonEnabled.style.backgroundColor);
5853                 }
5855                 var dockButtonDisabled = MUI.getCSSRule('.dockButtonDisabled');
5856                 if (dockButtonDisabled && dockButtonDisabled.style.backgroundColor){
5857                         this.options.disabledButtonColor = new Color(dockButtonDisabled.style.backgroundColor);
5858                 }
5860                 var trueButtonColor = MUI.getCSSRule('.dockButtonTrue');
5861                 if (trueButtonColor && trueButtonColor.style.backgroundColor){
5862                         this.options.trueButtonColor = new Color(trueButtonColor.style.backgroundColor);
5863                 }
5864         },
5866         renderDockControls: function(){
5867                 // Draw dock controls
5868                 var ctx = $('dockCanvas').getContext('2d');
5869                 ctx.clearRect(0, 0, 100, 100);
5870                 MUI.circle(ctx, 5 , 4, 3, this.options.enabledButtonColor, 1.0);
5872                 if( this.dockWrapper.getProperty('dockPosition') == 'top'){
5873                         MUI.circle(ctx, 5 , 14, 3, this.options.disabledButtonColor, 1.0)
5874                 }
5875                 else if (this.dockAutoHide){
5876                         MUI.circle(ctx, 5 , 14, 3, this.options.trueButtonColor, 1.0);
5877                 }
5878                 else {
5879                         MUI.circle(ctx, 5 , 14, 3, this.options.enabledButtonColor, 1.0);
5880                 }
5881         },
5883         moveDock: function(){
5884                         var ctx = $('dockCanvas').getContext('2d');
5885                         // Move dock to top position
5886                         if (this.dockWrapper.getStyle('position') != 'relative'){
5887                                 this.dockWrapper.setStyles({
5888                                         'position': 'relative',
5889                                         'bottom':   null
5890                                 });
5891                                 this.dockWrapper.addClass('top');
5892                                 MUI.Desktop.setDesktopSize();
5893                                 this.dockWrapper.setProperty('dockPosition','top');
5894                                 ctx.clearRect(0, 0, 100, 100);
5895                                 MUI.circle(ctx, 5, 4, 3, this.options.enabledButtonColor, 1.0);
5896                                 MUI.circle(ctx, 5, 14, 3, this.options.disabledButtonColor, 1.0);
5897                                 $('dockPlacement').setProperty('title', 'Position Dock Bottom');
5898                                 $('dockAutoHide').setProperty('title', 'Auto Hide Disabled in Top Dock Position');
5899                                 this.dockAutoHide = false;
5900                         }
5901                         // Move dock to bottom position
5902                         else {
5903                                 this.dockWrapper.setStyles({
5904                                         'position':      'absolute',
5905                                         'bottom':        MUI.Desktop.desktopFooter ? MUI.Desktop.desktopFooter.offsetHeight : 0
5906                                 });
5907                                 this.dockWrapper.removeClass('top');
5908                                 MUI.Desktop.setDesktopSize();
5909                                 this.dockWrapper.setProperty('dockPosition', 'bottom');
5910                                 ctx.clearRect(0, 0, 100, 100);
5911                                 MUI.circle(ctx, 5, 4, 3, this.options.enabledButtonColor, 1.0);
5912                                 MUI.circle(ctx, 5 , 14, 3, this.options.enabledButtonColor, 1.0);
5913                                 $('dockPlacement').setProperty('title', 'Position Dock Top');
5914                                 $('dockAutoHide').setProperty('title', 'Turn Auto Hide On');
5915                         }
5916         },
5918         createDockTab: function(windowEl){
5920                 var instance = windowEl.retrieve('instance');
5921                 var titleText = instance.titleEl.innerHTML;
5923                 var dockTab = new Element('div', {
5924                         'id': instance.options.id + '_dockTab',
5925                         'class': 'dockTab',
5926                         'title': titleText
5927                 }).inject($('dockClear'), 'before');
5929                 dockTab.addEvent('mousedown', function(e){
5930                         new Event(e).stop();
5931                         this.timeDown = $time();
5932                 });
5934                 dockTab.addEvent('mouseup', function(e){
5935                         this.timeUp = $time();
5936                         if ((this.timeUp - this.timeDown) < 275){
5937                                 // If the visibility of the windows on the page are toggled off, toggle visibility on.
5938                                 if (MUI.Windows.windowsVisible == false) {
5939                                         MUI.toggleWindowVisibility();
5940                                         if (instance.isMinimized == true) {
5941                                                 MUI.Dock.restoreMinimized.delay(25, MUI.Dock, windowEl);
5942                                         }
5943                                         else {
5944                                                 MUI.focusWindow(windowEl);
5945                                         }
5946                                         return;
5947                                 }
5948                                 // If window is minimized, restore window.
5949                                 if (instance.isMinimized == true) {
5950                                         MUI.Dock.restoreMinimized.delay(25, MUI.Dock, windowEl);
5951                                 }
5952                                 else{
5953                                         // If window is not minimized and is focused, minimize window.
5954                                         if (instance.windowEl.hasClass('isFocused') && instance.options.minimizable == true){
5955                                                 MUI.Dock.minimizeWindow(windowEl)
5956                                         }
5957                                         // If window is not minimized and is not focused, focus window.
5958                                         else{
5959                                                 MUI.focusWindow(windowEl);
5960                                         }
5961                                         // if the window is not minimized and is outside the viewport, center it in the viewport.
5962                                         var coordinates = document.getCoordinates();
5963                                         if (windowEl.getStyle('left').toInt() > coordinates.width || windowEl.getStyle('top').toInt() > coordinates.height){
5964                                                 MUI.centerWindow(windowEl);
5965                                         }
5966                                 }
5967                         }
5968                 });
5970                 this.dockSortables.addItems(dockTab);
5972                 var dockTabText = new Element('div', {
5973                         'id': instance.options.id + '_dockTabText',
5974                         'class': 'dockText'
5975                 }).set('html', titleText.substring(0,19) + (titleText.length > 19 ? '...' : '')).inject($(dockTab));
5977                 // If I implement this again, will need to also adjust the titleText truncate and the tab's
5978                 // left padding.
5979                 if (instance.options.icon != false){
5980                         // dockTabText.setStyle('background', 'url(' + instance.options.icon + ') 4px 4px no-repeat');
5981                 }
5983                 // Need to resize everything in case the dock wraps when a new tab is added
5984                 MUI.Desktop.setDesktopSize();
5986         },
5988         makeActiveTab: function(){
5990                 // getWindowWith HighestZindex is used in case the currently focused window
5991                 // is closed.
5992                 var windowEl = MUI.getWindowWithHighestZindex();
5993                 var instance = windowEl.retrieve('instance');
5995                 $$('.dockTab').removeClass('activeDockTab');
5996                 if (instance.isMinimized != true) {
5998                         instance.windowEl.addClass('isFocused');
6000                         var currentButton = $(instance.options.id + '_dockTab');
6001                         if (currentButton != null) {
6002                                 currentButton.addClass('activeDockTab');
6003                         }
6004                 }
6005                 else {
6006                         instance.windowEl.removeClass('isFocused');
6007                 }
6008         },
6010         minimizeWindow: function(windowEl){
6011                 if (windowEl != $(windowEl)) return;
6013                 var instance = windowEl.retrieve('instance');
6014                 instance.isMinimized = true;
6016                 // Hide iframe
6017                 // Iframe should be hidden when minimizing, maximizing, and moving for performance and Flash issues
6018                 if ( instance.iframeEl ) {
6019                         // Some elements are still visible in IE8 in the iframe when the iframe's visibility is set to hidden.
6020                         if (!MUI.ieLegacySupport) {
6021                                 instance.iframeEl.setStyle('visibility', 'hidden');
6022                         }
6023                         else {
6024                                 instance.iframeEl.hide();
6025                         }
6026                 }
6028                 // Hide window and add to dock
6029                 instance.contentBorderEl.setStyle('visibility', 'hidden');
6030                 if(instance.toolbarWrapperEl){
6031                         instance.toolbarWrapperEl.hide();
6032                 }
6033                 windowEl.setStyle('visibility', 'hidden');
6035                 MUI.Desktop.setDesktopSize();
6037                 // Have to use timeout because window gets focused when you click on the minimize button
6038                 setTimeout(function(){
6039                         windowEl.setStyle('zIndex', 1);
6040                         windowEl.removeClass('isFocused');
6041                         this.makeActiveTab();
6042                 }.bind(this),100);
6044                 instance.fireEvent('onMinimize', windowEl);
6045         },
6047         restoreMinimized: function(windowEl) {
6049                 var instance = windowEl.retrieve('instance');
6051                 if (instance.isMinimized == false) return;
6053                 if (MUI.Windows.windowsVisible == false){
6054                         MUI.toggleWindowVisibility();
6055                 }
6057                 MUI.Desktop.setDesktopSize();
6059                  // Part of Mac FF2 scrollbar fix
6060                 if (instance.options.scrollbars == true && !instance.iframeEl){
6061                         instance.contentWrapperEl.setStyle('overflow', 'auto');
6062                 }
6064                 if (instance.isCollapsed) {
6065                         MUI.collapseToggle(windowEl);
6066                 }
6068                 windowEl.setStyle('visibility', 'visible');
6069                 instance.contentBorderEl.setStyle('visibility', 'visible');
6070                 if(instance.toolbarWrapperEl){
6071                         instance.toolbarWrapperEl.show();
6072                 }
6074                 // Show iframe
6075                 if (instance.iframeEl){
6076                         if (!MUI.ieLegacySupport){
6077                                 instance.iframeEl.setStyle('visibility', 'visible');
6078                         }
6079                         else {
6080                                 instance.iframeEl.show();
6081                         }
6082                 }
6084                 instance.isMinimized = false;
6085                 MUI.focusWindow(windowEl);
6086                 instance.fireEvent('onRestore', windowEl);
6088         }
6092 Script: Workspaces.js
6093         Save and load workspaces. The Workspaces emulate Adobe Illustrator functionality remembering what windows are open and where they are positioned.
6095 Copyright:
6096         Copyright (c) 2007-2009 Greg Houston, <http://greghoustondesign.com/>.
6098 License:
6099         MIT-style license.
6101 Requires:
6102         Core.js, Window.js
6104 To do:
6105         - Move to Window
6109 MUI.files[MUI.path.source + 'Layout/Workspaces.js'] = 'loaded';
6111 MUI.extend({
6112         /*
6114         Function: saveWorkspace
6115                 Save the current workspace.
6117         Syntax:
6118         (start code)
6119                 MUI.saveWorkspace();
6120         (end)
6122         Notes:
6123                 This version saves the ID of each open window to a cookie, and reloads those windows using the functions in mocha-init.js. This requires that each window have a function in mocha-init.js used to open them. Functions must be named the windowID + "Window". So if your window is called mywindow, it needs a function called mywindowWindow in mocha-init.js.
6125         */
6126         saveWorkspace: function(){
6127                 this.cookie = new Hash.Cookie('mochaUIworkspaceCookie', {duration: 3600});
6128                 this.cookie.empty();
6129                 MUI.Windows.instances.each(function(instance) {
6130                         instance.saveValues();
6131                         this.cookie.set(instance.options.id, {
6132                                 'id': instance.options.id,
6133                                 'top': instance.options.y,
6134                                 'left': instance.options.x,
6135                                 'width': instance.contentWrapperEl.getStyle('width').toInt(),
6136                                 'height': instance.contentWrapperEl.getStyle('height').toInt()
6137                         });
6138                 }.bind(this));
6139                 this.cookie.save();
6141                 new MUI.Window({
6142                         loadMethod: 'html',
6143                         type: 'notification',
6144                         addClass: 'notification',
6145                         content: 'Workspace saved.',
6146                         closeAfter: '1400',
6147                         width: 200,
6148                         height: 40,
6149                         y: 53,
6150                         padding:  { top: 10, right: 12, bottom: 10, left: 12 },
6151                         shadowBlur: 5,
6152                         bodyBgColor: [255, 255, 255]
6153                 });
6155         },
6156         windowUnload: function(){
6157                 if ($$('.mocha').length == 0 && this.myChain){
6158                         this.myChain.callChain();
6159                 }
6160         },
6161         loadWorkspace2: function(workspaceWindows){
6162                 workspaceWindows.each(function(workspaceWindow){
6163                         windowFunction = eval('MUI.' + workspaceWindow.id + 'Window');
6164                         if (windowFunction){
6165                                 eval('MUI.' + workspaceWindow.id + 'Window({width:'+ workspaceWindow.width +',height:' + workspaceWindow.height + '});');
6166                                 var windowEl = $(workspaceWindow.id);
6167                                 windowEl.setStyles({
6168                                         'top': workspaceWindow.top,
6169                                         'left': workspaceWindow.left
6170                                 });
6171                                 var instance = windowEl.retrieve('instance');
6172                                 instance.contentWrapperEl.setStyles({
6173                                         'width': workspaceWindow.width,
6174                                         'height': workspaceWindow.height
6175                                 });
6176                                 instance.drawWindow();
6177                         }
6178                 }.bind(this));
6179                 this.loadingWorkspace = false;
6180         },
6181         /*
6183         Function: loadWorkspace
6184                 Load the saved workspace.
6186         Syntax:
6187         (start code)
6188                 MUI.loadWorkspace();
6189         (end)
6191         */
6192         loadWorkspace: function(){
6193                 cookie = new Hash.Cookie('mochaUIworkspaceCookie', {duration: 3600});
6194                 workspaceWindows = cookie.load();
6196                 if(!cookie.getKeys().length){
6197                         new MUI.Window({
6198                                 loadMethod: 'html',
6199                                 type: 'notification',
6200                                 addClass: 'notification',
6201                                 content: 'You have no saved workspace.',
6202                                 closeAfter: '1400',
6203                                 width: 220,
6204                                 height: 40,
6205                                 y: 25,
6206                                 padding:  { top: 10, right: 12, bottom: 10, left: 12 },
6207                                 shadowBlur: 5,
6208                                 bodyBgColor: [255, 255, 255]
6209                         });
6210                         return;
6211                 }
6213                 if ($$('.mocha').length != 0){
6214                         this.loadingWorkspace = true;
6215                         this.myChain = new Chain();
6216                         this.myChain.chain(
6217                                 function(){
6218                                         $$('.mocha').each(function(el) {
6219                                                 this.closeWindow(el);
6220                                         }.bind(this));
6221                                 }.bind(this),
6222                                 function(){
6223                                         this.loadWorkspace2(workspaceWindows);
6224                                 }.bind(this)
6225                         );
6226                         this.myChain.callChain();
6227                 }
6228                 else {
6229                         this.loadWorkspace2(workspaceWindows);
6230                 }
6232         }