]> jfr.im git - irc/quakenet/qwebirc.git/blob - static/js/mochaui/mocha.js
Update to MochaUI 0.9.5.
[irc/quakenet/qwebirc.git] / static / js / mochaui / mocha.js
1 /*
2
3 Script: Core.js
4 MochaUI - A Web Applications User Interface Framework.
5
6 Copyright:
7 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
8
9 License:
10 MIT-style license.
11
12 Contributors:
13 - Scott F. Frederick
14 - Joel Lindau
15
16 Note:
17 This documentation is taken directly from the javascript source files. It is built using Natural Docs.
18
19 Todo:
20 Consider making title tooltips optional and using them more often.
21
22 */
23
24 var MochaUI = new Hash({
25 options: new Hash({
26 useEffects: true // Toggles the majority of window fade and move effects.
27 }),
28 Columns: {
29 instances: new Hash()
30 },
31 Panels: {
32 instances: new Hash()
33 },
34 Windows: {
35 instances: new Hash(),
36 indexLevel: 100, // Used for z-Index
37 windowIDCount: 0, // Used for windows without an ID defined by the user
38 windowsVisible: true // Ctrl-Alt-Q to toggle window visibility
39 },
40 ieSupport: 'excanvas', // Makes it easier to switch between Excanvas and Moocanvas for testing
41 focusingWindow: 'false',
42 /*
43
44 Function: updateContent
45 Replace the content of a window or panel.
46
47 Arguments:
48 element - The parent window or panel.
49 childElement - The child element of the window or panel recieving the content.
50 title - (string) Change this if you want to change the title of the window or panel.
51 content - (string or element) An html loadMethod option.
52 loadMethod - ('html', 'xhr', or 'iframe') Defaults to 'html'.
53 url - Used if loadMethod is set to 'xhr' or 'iframe'.
54 padding - (object)
55
56 */
57 updateContent: function(updateOptions){
58
59 var options = {
60 'element': null,
61 'childElement': null,
62 'title': null,
63 'content': null,
64 'loadMethod': null,
65 'url': null,
66 'padding': null
67 };
68 $extend(options, updateOptions);
69
70 if (!options.element) return;
71 var element = options.element;
72
73 if (MochaUI.Windows.instances.get(element.id)) {
74 var recipient = 'window';
75 var currentInstance = MochaUI.Windows.instances.get(element.id);
76 var spinnerEl = currentInstance.spinnerEl;
77 if (options.title) {
78 currentInstance.titleEl.set('html', options.title);
79 }
80 }
81 else {
82 var recipient = 'panel';
83 var currentInstance = MochaUI.Panels.instances.get(element.id);
84 if (options.title) {
85 currentInstance.titleEl.set('html', options.title);
86 }
87 }
88
89 var contentEl = currentInstance.contentEl;
90 if (options.childElement != null) {
91 var contentContainer = options.childElement;
92 }
93 else {
94 var contentContainer = currentInstance.contentEl;
95 }
96
97 var loadMethod = options.loadMethod != null ? options.loadMethod : currentInstance.options.loadMethod;
98
99 // Set scrollbars if loading content in main content container.
100 // Always use 'hidden' for iframe windows
101 if (contentContainer == currentInstance.contentEl) {
102 currentInstance.contentWrapperEl.setStyles({
103 'overflow': currentInstance.options.scrollbars == true && loadMethod != 'iframe' ? 'auto' : 'hidden'
104 });
105 }
106
107 var contentWrapperEl = currentInstance.contentWrapperEl;
108
109 if (options.padding != null) {
110 contentEl.setStyles({
111 'padding-top': options.padding.top,
112 'padding-bottom': options.padding.bottom,
113 'padding-left': options.padding.left,
114 'padding-right': options.padding.right
115 });
116 }
117
118 // Remove old content.
119 if (contentContainer == contentEl){
120 contentEl.empty();
121 }
122
123 // Load new content.
124 switch(loadMethod){
125 case 'xhr':
126 new Request.HTML({
127 url: options.url,
128 update: contentContainer,
129 evalScripts: currentInstance.options.evalScripts,
130 evalResponse: currentInstance.options.evalResponse,
131 onRequest: function(){
132 if (recipient == 'window' && contentContainer == contentEl){
133 currentInstance.showSpinner(spinnerEl);
134 }
135 else if (recipient == 'panel' && contentContainer == contentEl && $('spinner')){
136 $('spinner').setStyle('visibility','visible');
137 }
138 }.bind(this),
139 onFailure: function(){
140 if (contentContainer == contentEl){
141 contentContainer.set('html','<p><strong>Error Loading XMLHttpRequest</strong></p>');
142 if (recipient == 'window') {
143 currentInstance.hideSpinner(spinnerEl);
144 }
145 else if (recipient == 'panel' && $('spinner')) {
146 $('spinner').setStyle('visibility', 'hidden');
147 }
148 }
149 }.bind(this),
150 onException: function(){}.bind(this),
151 onSuccess: function(){
152 if (contentContainer == contentEl){
153 if (recipient == 'window'){
154 currentInstance.hideSpinner(spinnerEl);
155 }
156 else if (recipient == 'panel' && $('spinner')){
157 $('spinner').setStyle('visibility', 'hidden');
158 }
159 currentInstance.fireEvent('onContentLoaded', element);
160 }
161 }.bind(this),
162 onComplete: function(){}.bind(this)
163 }).get();
164 break;
165 case 'iframe': // May be able to streamline this if the iframe already exists.
166 if ( currentInstance.options.contentURL == '' || contentContainer != contentEl) {
167 break;
168 }
169 currentInstance.iframeEl = new Element('iframe', {
170 'id': currentInstance.options.id + '_iframe',
171 'name': currentInstance.options.id + '_iframe',
172 'class': 'mochaIframe',
173 'src': options.url,
174 'marginwidth': 0,
175 'marginheight': 0,
176 'frameBorder': 0,
177 'scrolling': 'auto',
178 'styles': {
179 'height': contentWrapperEl.offsetHeight - contentWrapperEl.getStyle('border-top').toInt() - contentWrapperEl.getStyle('border-bottom').toInt(),
180 'width': currentInstance.panelEl ? contentWrapperEl.offsetWidth - contentWrapperEl.getStyle('border-left').toInt() - contentWrapperEl.getStyle('border-right').toInt() : '100%'
181 }
182 }).injectInside(contentEl);
183
184 // Add onload event to iframe so we can hide the spinner and run onContentLoaded()
185 currentInstance.iframeEl.addEvent('load', function(e) {
186 if (recipient == 'window') {
187 currentInstance.hideSpinner(spinnerEl);
188 }
189 else if (recipient == 'panel' && contentContainer == contentEl && $('spinner')) {
190 $('spinner').setStyle('visibility', 'hidden');
191 }
192 currentInstance.fireEvent('onContentLoaded', element);
193 }.bind(this));
194 if (recipient == 'window') {
195 currentInstance.showSpinner(spinnerEl);
196 }
197 else if (recipient == 'panel' && contentContainer == contentEl && $('spinner')){
198 $('spinner').setStyle('visibility', 'visible');
199 }
200 break;
201 case 'html':
202 default:
203 // Need to test injecting elements as content.
204 var elementTypes = new Array('element', 'textnode', 'whitespace', 'collection');
205 if (elementTypes.contains($type(options.content))){
206 options.content.inject(contentContainer);
207 } else {
208 contentContainer.set('html', options.content);
209 }
210 currentInstance.fireEvent('onContentLoaded', element);
211 break;
212 }
213
214 },
215 /*
216
217 Function: reloadIframe
218 Reload an iframe. Fixes an issue in Firefox when trying to use location.reload on an iframe that has been destroyed and recreated.
219
220 Arguments:
221 iframe - This should be both the name and the id of the iframe.
222
223 Syntax:
224 (start code)
225 MochaUI.reloadIframe(element);
226 (end)
227
228 Example:
229 To reload an iframe from within another iframe:
230 (start code)
231 parent.MochaUI.reloadIframe('myIframeName');
232 (end)
233
234 */
235 reloadIframe: function(iframe){
236 if (Browser.Engine.gecko) {
237 $(iframe).src = $(iframe).src;
238 }
239 else {
240 top.frames[iframe].location.reload(true);
241 }
242 },
243 collapseToggle: function(windowEl){
244 var instances = MochaUI.Windows.instances;
245 var currentInstance = instances.get(windowEl.id);
246 var handles = currentInstance.windowEl.getElements('.handle');
247 if (currentInstance.isMaximized == true) return;
248 if (currentInstance.isCollapsed == false) {
249 currentInstance.isCollapsed = true;
250 handles.setStyle('display', 'none');
251 if ( currentInstance.iframeEl ) {
252 currentInstance.iframeEl.setStyle('visibility', 'hidden');
253 }
254 currentInstance.contentBorderEl.setStyles({
255 visibility: 'hidden',
256 position: 'absolute',
257 top: -10000,
258 left: -10000
259 });
260 if(currentInstance.toolbarWrapperEl){
261 currentInstance.toolbarWrapperEl.setStyles({
262 visibility: 'hidden',
263 position: 'absolute',
264 top: -10000,
265 left: -10000
266 });
267 }
268 currentInstance.drawWindowCollapsed(windowEl);
269 }
270 else {
271 currentInstance.isCollapsed = false;
272 currentInstance.drawWindow(windowEl);
273 currentInstance.contentBorderEl.setStyles({
274 visibility: 'visible',
275 position: null,
276 top: null,
277 left: null
278 });
279 if(currentInstance.toolbarWrapperEl){
280 currentInstance.toolbarWrapperEl.setStyles({
281 visibility: 'visible',
282 position: null,
283 top: null,
284 left: null
285 });
286 }
287 if ( currentInstance.iframeEl ) {
288 currentInstance.iframeEl.setStyle('visibility', 'visible');
289 }
290 handles.setStyle('display', 'block');
291 }
292 },
293 /*
294
295 Function: closeWindow
296 Closes a window.
297
298 Syntax:
299 (start code)
300 MochaUI.closeWindow();
301 (end)
302
303 Arguments:
304 windowEl - the ID of the window to be closed
305
306 Returns:
307 true - the window was closed
308 false - the window was not closed
309
310 */
311 closeWindow: function(windowEl){
312 // Does window exist and is not already in process of closing ?
313
314 var instances = MochaUI.Windows.instances;
315 var currentInstance = instances.get(windowEl.id);
316 if (windowEl != $(windowEl) || currentInstance.isClosing) return;
317
318 currentInstance.isClosing = true;
319 currentInstance.fireEvent('onClose', windowEl);
320 if (currentInstance.check) currentInstance.check.destroy();
321
322 if ((currentInstance.options.type == 'modal' || currentInstance.options.type == 'modal2') && Browser.Engine.trident4){
323 $('modalFix').setStyle('display', 'none');
324 }
325
326 if (MochaUI.options.useEffects == false){
327 if (currentInstance.options.type == 'modal' || currentInstance.options.type == 'modal2'){
328 $('modalOverlay').setStyle('opacity', 0);
329 }
330 MochaUI.closingJobs(windowEl);
331 return true;
332 }
333 else {
334 // Redraws IE windows without shadows since IE messes up canvas alpha when you change element opacity
335 if (Browser.Engine.trident) currentInstance.drawWindow(windowEl, false);
336 if (currentInstance.options.type == 'modal' || currentInstance.options.type == 'modal2'){
337 MochaUI.Modal.modalOverlayCloseMorph.start({
338 'opacity': 0
339 });
340 }
341 var closeMorph = new Fx.Morph(windowEl, {
342 duration: 120,
343 onComplete: function(){
344 MochaUI.closingJobs(windowEl);
345 return true;
346 }.bind(this)
347 });
348 closeMorph.start({
349 'opacity': .4
350 });
351 }
352
353 },
354 closingJobs: function(windowEl){
355
356 var instances = MochaUI.Windows.instances;
357 var currentInstance = instances.get(windowEl.id);
358 windowEl.setStyle('visibility', 'hidden');
359 windowEl.destroy();
360 currentInstance.fireEvent('onCloseComplete');
361
362 if (currentInstance.options.type != 'notification'){
363 var newFocus = this.getWindowWithHighestZindex();
364 this.focusWindow(newFocus);
365 }
366
367 instances.erase(currentInstance.options.id);
368 if (this.loadingWorkspace == true) {
369 this.windowUnload();
370 }
371
372 if (MochaUI.Dock && $(MochaUI.options.dock) && currentInstance.options.type == 'window') {
373 var currentButton = $(currentInstance.options.id + '_dockTab');
374 if (currentButton != null) {
375 MochaUI.Dock.dockSortables.removeItems(currentButton).destroy();
376 }
377 // Need to resize everything in case the dock becomes smaller when a tab is removed
378 MochaUI.Desktop.setDesktopSize();
379 }
380 },
381 /*
382
383 Function: closeAll
384 Close all open windows.
385
386 */
387 closeAll: function() {
388 $$('div.mocha').each(function(windowEl){
389 this.closeWindow(windowEl);
390 }.bind(this));
391 },
392 /*
393
394 Function: toggleWindowVisibility
395 Toggle window visibility with Ctrl-Alt-Q.
396
397 */
398 toggleWindowVisibility: function(){
399 MochaUI.Windows.instances.each(function(instance){
400 if (instance.options.type == 'modal' || instance.options.type == 'modal2' || instance.isMinimized == true) return;
401 var id = $(instance.options.id);
402 if (id.getStyle('visibility') == 'visible'){
403 if (instance.iframe){
404 instance.iframeEl.setStyle('visibility', 'hidden');
405 }
406 if (instance.toolbarEl){
407 instance.toolbarWrapperEl.setStyle('visibility', 'hidden');
408 }
409 instance.contentBorderEl.setStyle('visibility', 'hidden');
410
411 id.setStyle('visibility', 'hidden');
412 MochaUI.Windows.windowsVisible = false;
413 }
414 else {
415 id.setStyle('visibility', 'visible');
416 instance.contentBorderEl.setStyle('visibility', 'visible');
417 if (instance.iframe){
418 instance.iframeEl.setStyle('visibility', 'visible');
419 }
420 if (instance.toolbarEl){
421 instance.toolbarWrapperEl.setStyle('visibility', 'visible');
422 }
423 MochaUI.Windows.windowsVisible = true;
424 }
425 }.bind(this));
426
427 },
428 focusWindow: function(windowEl, fireEvent){
429
430 // This is used with blurAll
431 MochaUI.focusingWindow = 'true';
432 var windowClicked = function(){
433 MochaUI.focusingWindow = 'false';
434 };
435 windowClicked.delay(170, this);
436
437 // Only focus when needed
438 if ($$('.mocha').length == 0) return;
439 if (windowEl != $(windowEl) || windowEl.hasClass('isFocused')) return;
440
441 var instances = MochaUI.Windows.instances;
442 var currentInstance = instances.get(windowEl.id);
443
444 if (currentInstance.options.type == 'notification') return;
445
446 MochaUI.Windows.indexLevel += 2;
447 windowEl.setStyle('zIndex', MochaUI.Windows.indexLevel);
448
449 // Used when dragging and resizing windows
450 $('windowUnderlay').setStyle('zIndex', MochaUI.Windows.indexLevel - 1).inject($(windowEl),'after');
451
452 // Fire onBlur for the window that lost focus.
453 instances.each(function(instance){
454 if (instance.windowEl.hasClass('isFocused')){
455 instance.fireEvent('onBlur', instance.windowEl);
456 }
457 instance.windowEl.removeClass('isFocused');
458 });
459
460 if (MochaUI.Dock && $(MochaUI.options.dock) && currentInstance.options.type == 'window') {
461 MochaUI.Dock.makeActiveTab();
462 }
463 currentInstance.windowEl.addClass('isFocused');
464
465 if (fireEvent != false){
466 currentInstance.fireEvent('onFocus', windowEl);
467 }
468
469 },
470 getWindowWithHighestZindex: function(){
471 this.highestZindex = 0;
472 $$('div.mocha').each(function(element){
473 this.zIndex = element.getStyle('zIndex');
474 if (this.zIndex >= this.highestZindex) {
475 this.highestZindex = this.zIndex;
476 }
477 }.bind(this));
478 $$('div.mocha').each(function(element){
479 if (element.getStyle('zIndex') == this.highestZindex) {
480 this.windowWithHighestZindex = element;
481 }
482 }.bind(this));
483 return this.windowWithHighestZindex;
484 },
485 blurAll: function(){
486 if (MochaUI.focusingWindow == 'false') {
487 $$('.mocha').each(function(windowEl){
488 var instances = MochaUI.Windows.instances;
489 var currentInstance = instances.get(windowEl.id);
490 if (currentInstance.options.type != 'modal' && currentInstance.options.type != 'modal2'){
491 windowEl.removeClass('isFocused');
492 }
493 });
494 $$('div.dockTab').removeClass('activeDockTab');
495 }
496 },
497 roundedRect: function(ctx, x, y, width, height, radius, rgb, a){
498 ctx.fillStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
499 ctx.beginPath();
500 ctx.moveTo(x, y + radius);
501 ctx.lineTo(x, y + height - radius);
502 ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
503 ctx.lineTo(x + width - radius, y + height);
504 ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
505 ctx.lineTo(x + width, y + radius);
506 ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
507 ctx.lineTo(x + radius, y);
508 ctx.quadraticCurveTo(x, y, x, y + radius);
509 ctx.fill();
510 },
511 triangle: function(ctx, x, y, width, height, rgb, a){
512 ctx.beginPath();
513 ctx.moveTo(x + width, y);
514 ctx.lineTo(x, y + height);
515 ctx.lineTo(x + width, y + height);
516 ctx.closePath();
517 ctx.fillStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
518 ctx.fill();
519 },
520 circle: function(ctx, x, y, diameter, rgb, a){
521 ctx.beginPath();
522 ctx.moveTo(x, y);
523 ctx.arc(x, y, diameter, 0, Math.PI*2, true);
524 ctx.fillStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
525 ctx.fill();
526 },
527 /*
528
529 Function: centerWindow
530 Center a window in it's container. If windowEl is undefined it will center the window that has focus.
531
532 */
533 centerWindow: function(windowEl){
534
535 if(!windowEl){
536 MochaUI.Windows.instances.each(function(instance){
537 if (instance.windowEl.hasClass('isFocused')){
538 windowEl = instance.windowEl;
539 }
540 });
541 }
542
543 var currentInstance = MochaUI.Windows.instances.get(windowEl.id);
544 var options = currentInstance.options;
545 var dimensions = options.container.getCoordinates();
546 var windowPosTop = (dimensions.height * .5) - ((options.height + currentInstance.headerFooterShadow) * .5);
547 if (windowPosTop < 0) {
548 windowPosTop = 0;
549 }
550 var windowPosLeft = (dimensions.width * .5) - (options.width * .5);
551 if (windowPosLeft < 0) {
552 windowPosLeft = 0;
553 }
554 if (MochaUI.options.useEffects == true){
555 currentInstance.morph.start({
556 'top': windowPosTop,
557 'left': windowPosLeft
558 });
559 }
560 else {
561 windowEl.setStyles({
562 'top': windowPosTop,
563 'left': windowPosLeft
564 });
565 }
566 },
567 notification: function(message){
568 new MochaUI.Window({
569 loadMethod: 'html',
570 closeAfter: 1500,
571 type: 'notification',
572 addClass: 'notification',
573 content: message,
574 width: 220,
575 height: 40,
576 y: 53,
577 padding: { top: 10, right: 12, bottom: 10, left: 12 },
578 shadowBlur: 5,
579 bodyBgColor: [255, 255, 255]
580 });
581 },
582 /*
583
584 Function: dynamicResize
585 Use with a timer to resize a window as the window's content size changes, such as with an accordian.
586
587 */
588 dynamicResize: function(windowEl){
589 var currentInstance = MochaUI.Windows.instances.get(windowEl.id);
590 var contentWrapperEl = currentInstance.contentWrapperEl;
591 var contentEl = currentInstance.contentEl;
592
593 contentWrapperEl.setStyle('height', contentEl.offsetHeight);
594 contentWrapperEl.setStyle('width', contentEl.offsetWidth);
595 currentInstance.drawWindow(windowEl);
596 },
597 /*
598
599 Function: garbageCleanUp
600 Empties all windows of their children, and removes and garbages the windows. It is does not trigger onClose() or onCloseComplete(). This is useful to clear memory before the pageUnload.
601
602 Syntax:
603 (start code)
604 MochaUI.garbageCleanUp();
605 (end)
606
607 */
608 garbageCleanUp: function(){
609 $$('div.mocha').each(function(el){
610 el.destroy();
611 }.bind(this));
612 },
613 /*
614
615 The underlay is inserted directly under windows when they are being dragged or resized
616 so that the cursor is not captured by iframes or other plugins (such as Flash)
617 underneath the window.
618
619 */
620 underlayInitialize: function(){
621 var windowUnderlay = new Element('div', {
622 'id': 'windowUnderlay',
623 'styles': {
624 'height': parent.getCoordinates().height,
625 'opacity': .01,
626 'display': 'none'
627 }
628 }).inject(document.body);
629 },
630 setUnderlaySize: function(){
631 $('windowUnderlay').setStyle('height', parent.getCoordinates().height);
632 }
633 });
634
635 /*
636
637 function: fixPNG
638 Bob Osola's PngFix for IE6.
639
640 example:
641 (begin code)
642 <img src="xyz.png" alt="foo" width="10" height="20" onload="fixPNG(this)">
643 (end)
644
645 note:
646 You must have the image height and width attributes specified in the markup.
647
648 */
649
650 function fixPNG(myImage){
651 if (Browser.Engine.trident4 && document.body.filters){
652 var imgID = (myImage.id) ? "id='" + myImage.id + "' " : "";
653 var imgClass = (myImage.className) ? "class='" + myImage.className + "' " : "";
654 var imgTitle = (myImage.title) ? "title='" + myImage.title + "' " : "title='" + myImage.alt + "' ";
655 var imgStyle = "display:inline-block;" + myImage.style.cssText;
656 var strNewHTML = "<span " + imgID + imgClass + imgTitle
657 + " style=\"" + "width:" + myImage.width
658 + "px; height:" + myImage.height
659 + "px;" + imgStyle + ";"
660 + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader"
661 + "(src=\'" + myImage.src + "\', sizingMethod='scale');\"></span>";
662 myImage.outerHTML = strNewHTML;
663 }
664 }
665
666 // Toggle window visibility with Ctrl-Alt-Q
667 document.addEvent('keydown', function(event){
668 if (event.key == 'q' && event.control && event.alt) {
669 MochaUI.toggleWindowVisibility();
670 }
671 });
672
673 // Blur all windows if user clicks anywhere else on the page
674 document.addEvent('mousedown', function(event){
675 MochaUI.blurAll.delay(50);
676 });
677
678 document.addEvent('domready', function(){
679 MochaUI.underlayInitialize();
680 });
681
682 window.addEvent('resize', function(){
683 MochaUI.setUnderlaySize();
684 });
685 /*
686
687 Script: Window.js
688 Build windows.
689
690 Copyright:
691 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
692
693 License:
694 MIT-style license.
695
696 Requires:
697 Core.js
698
699 */
700
701 /*
702 Class: Window
703 Creates a single MochaUI window.
704
705 Syntax:
706 (start code)
707 new MochaUI.Window(options);
708 (end)
709
710 Arguments:
711 options
712
713 Options:
714 id - The ID of the window. If not defined, it will be set to 'win' + windowIDCount.
715 title - The title of the window.
716 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.
717 type - ('window', 'modal', 'modal2', or 'notification') Defaults to 'window'.
718 loadMethod - ('html', 'xhr', or 'iframe') Defaults to 'html'.
719 contentURL - Used if loadMethod is set to 'xhr' or 'iframe'.
720 closeAfter - Either false or time in milliseconds. Closes the window after a certain period of time in milliseconds. This is particularly useful for notifications.
721 evalScripts - (boolean) An xhr loadMethod option. Defaults to true.
722 evalResponse - (boolean) An xhr loadMethod option. Defaults to false.
723 content - (string or element) An html loadMethod option.
724 toolbar - (boolean) Create window toolbar. Defaults to false. This can be used for tabs, media controls, and so forth.
725 toolbarPosition - ('top' or 'bottom') Defaults to top.
726 toolbarHeight - (number)
727 toolbarURL - (url) Defaults to 'pages/lipsum.html'.
728 toolbarContent - (string)
729 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.
730 restrict - (boolean) Restrict window to container when dragging.
731 shape - ('box' or 'gauge') Shape of window. Defaults to 'box'.
732 collapsible - (boolean) Defaults to true.
733 minimizable - (boolean) Requires MochaUI.Desktop and MochaUI.Dock. Defaults to true if dependenices are met.
734 maximizable - (boolean) Requires MochaUI.Desktop. Defaults to true if dependenices are met.
735 closable - (boolean) Defaults to true.
736 draggable - (boolean) Defaults to false for modals; otherwise true.
737 draggableGrid - (false or number) Distance in pixels for snap-to-grid dragging. Defaults to false.
738 draggableLimit - (false or number) An object with x and y properties used to limit the movement of the Window. Defaults to false.
739 draggableSnap - (boolean) The distance to drag before the Window starts to respond to the drag. Defaults to false.
740 resizable - (boolean) Defaults to false for modals, notifications and gauges; otherwise true.
741 resizeLimit - (object) Minimum and maximum width and height of window when resized.
742 addClass - (string) Add a class to the window for more control over styling.
743 width - (number) Width of content area.
744 height - (number) Height of content area.
745 x - (number) If x and y are left undefined the window is centered on the page.
746 y - (number)
747 scrollbars - (boolean)
748 padding - (object)
749 shadowBlur - (number) Width of shadows.
750 shadowOffset - Should be positive and not be greater than the ShadowBlur.
751 controlsOffset - Change this if you want to reposition the window controls.
752 useCanvas - (boolean) Set this to false if you don't want a canvas body.
753 useCanvasControls - (boolean) Set this to false if you wish to use images for the buttons.
754 headerHeight - (number) Height of window titlebar.
755 footerHeight - (number) Height of window footer.
756 cornerRadius - (number)
757 contentBgColor - (hex) Body background color
758 headerStartColor - ([r,g,b,]) Titlebar gradient's top color
759 headerStopColor - ([r,g,b,]) Titlebar gradient's bottom color
760 bodyBgColor - ([r,g,b,]) Background color of the main canvas shape
761 minimizeBgColor - ([r,g,b,]) Minimize button background color
762 minimizeColor - ([r,g,b,]) Minimize button color
763 maximizeBgColor - ([r,g,b,]) Maximize button background color
764 maximizeColor - ([r,g,b,]) Maximize button color
765 closeBgColor - ([r,g,b,]) Close button background color
766 closeColor - ([r,g,b,]) Close button color
767 resizableColor - ([r,g,b,]) Resizable icon color
768 onBeforeBuild - (function) Fired just before the window is built.
769 onContentLoaded - (function) Fired when content is successfully loaded via XHR or Iframe.
770 onFocus - (function) Fired when the window is focused.
771 onBlur - (function) Fired when window loses focus.
772 onResize - (function) Fired when the window is resized.
773 onMinimize - (function) Fired when the window is minimized.
774 onMaximize - (function) Fired when the window is maximized.
775 onRestore - (function) Fired when a window is restored from minimized or maximized.
776 onClose - (function) Fired just before the window is closed.
777 onCloseComplete - (function) Fired after the window is closed.
778
779 Returns:
780 Window object.
781
782 Example:
783 Define a window. It is suggested you name the function the same as your window ID + "Window".
784 (start code)
785 var mywindowWindow = function(){
786 new MochaUI.Window({
787 id: 'mywindow',
788 title: 'My Window',
789 loadMethod: 'xhr',
790 contentURL: 'pages/lipsum.html',
791 width: 340,
792 height: 150
793 });
794 }
795 (end)
796
797 Example:
798 Create window onDomReady.
799 (start code)
800 window.addEvent('domready', function(){
801 mywindow();
802 });
803 (end)
804
805 Example:
806 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.
807
808 If you wish to add links in windows that open other windows remember to add events to those links when the windows are created.
809
810 (start code)
811 // Javascript:
812 if ($('mywindowLink')){
813 $('mywindowLink').addEvent('click', function(e) {
814 new Event(e).stop();
815 mywindow();
816 });
817 }
818
819 // HTML:
820 <a id="mywindowLink" href="pages/lipsum.html">My Window</a>
821 (end)
822
823
824 Loading Content with an XMLHttpRequest(xhr):
825 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.
826
827 Iframes:
828 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.
829
830 */
831
832 // Having these options outside of the Class allows us to add, change, and remove
833 // individual options without rewriting all of them.
834
835 MochaUI.Windows.windowOptions = {
836 id: null,
837 title: 'New Window',
838 icon: false,
839 type: 'window',
840
841 loadMethod: 'html',
842 contentURL: 'pages/lipsum.html',
843
844 closeAfter: false,
845
846 // xhr options
847 evalScripts: true,
848 evalResponse: false,
849
850 // html options
851 content: 'Window content',
852
853 // Toolbar
854 toolbar: false,
855 toolbarPosition: 'top',
856 toolbarHeight: 29,
857 toolbarURL: 'pages/lipsum.html',
858 toolbarContent: '',
859
860 // Toolbar
861 toolbar2: false,
862 toolbar2Position: 'bottom',
863 toolbar2Height: 29,
864 toolbar2URL: 'pages/lipsum.html',
865 toolbar2Content: '',
866
867 // Container options
868 container: null,
869 restrict: true,
870 shape: 'box',
871
872 // Window Controls
873 collapsible: true,
874 minimizable: true,
875 maximizable: true,
876 closable: true,
877
878 // Draggable
879 draggable: null,
880 draggableGrid: false,
881 draggableLimit: false,
882 draggableSnap: false,
883
884 // Resizable
885 resizable: null,
886 resizeLimit: {'x': [250, 2500], 'y': [125, 2000]},
887
888 // Style options:
889 addClass: '',
890 width: 300,
891 height: 125,
892 x: null,
893 y: null,
894 scrollbars: true,
895 padding: { top: 10, right: 12, bottom: 10, left: 12 },
896 shadowBlur: 5,
897 shadowOffset: {'x': 0, 'y': 1},
898 controlsOffset: {'right': 6, 'top': 6},
899 useCanvas: true,
900 useCanvasControls: true,
901 useSpinner: true, // Toggles whether or not the ajax spinners are displayed in window footers.
902
903 // Color options:
904 headerHeight: 25,
905 footerHeight: 25,
906 cornerRadius: 8,
907 contentBgColor: '#fff',
908 headerStartColor: [250, 250, 250],
909 headerStopColor: [229, 229, 229],
910 bodyBgColor: [229, 229, 229],
911 minimizeBgColor: [255, 255, 255],
912 minimizeColor: [0, 0, 0],
913 maximizeBgColor: [255, 255, 255],
914 maximizeColor: [0, 0, 0],
915 closeBgColor: [255, 255, 255],
916 closeColor: [0, 0, 0],
917 resizableColor: [254, 254, 254],
918
919 // Events
920 onBeforeBuild: $empty,
921 onContentLoaded: $empty,
922 onFocus: $empty,
923 onBlur: $empty,
924 onResize: $empty,
925 onMinimize: $empty,
926 onMaximize: $empty,
927 onRestore: $empty,
928 onClose: $empty,
929 onCloseComplete: $empty
930 };
931
932 MochaUI.Window = new Class({
933 options: MochaUI.Windows.windowOptions,
934 initialize: function(options){
935 this.setOptions(options);
936
937 // Shorten object chain
938 var options = this.options;
939
940 $extend(this, {
941 mochaControlsWidth: 0,
942 minimizebuttonX: 0, // Minimize button horizontal position
943 maximizebuttonX: 0, // Maximize button horizontal position
944 closebuttonX: 0, // Close button horizontal position
945 headerFooterShadow: options.headerHeight + options.footerHeight + (options.shadowBlur * 2),
946 oldTop: 0,
947 oldLeft: 0,
948 isMaximized: false,
949 isMinimized: false,
950 isCollapsed: false,
951 timestamp: $time()
952 });
953
954 // May be better to use if type != window
955 if (options.type != 'window'){
956 options.container = document.body;
957 options.minimizable = false;
958 }
959 if (!options.container){
960 options.container = MochaUI.Desktop.desktop ? MochaUI.Desktop.desktop : document.body;
961 }
962
963 // Set this.options.resizable to default if it was not defined
964 if (options.resizable == null){
965 if (options.type != 'window' || options.shape == 'gauge'){
966 options.resizable = false;
967 }
968 else {
969 options.resizable = true;
970 }
971 }
972
973 // Set this.options.draggable if it was not defined
974 if (options.draggable == null){
975 if (options.type != 'window'){
976 options.draggable = false;
977 }
978 else {
979 options.draggable = true;
980 }
981 }
982
983 // Gauges are not maximizable or resizable
984 if (options.shape == 'gauge' || options.type == 'notification'){
985 options.collapsible = false;
986 options.maximizable = false;
987 options.contentBgColor = 'transparent';
988 options.scrollbars = false;
989 options.footerHeight = 0;
990 }
991 if (options.type == 'notification'){
992 options.closable = false;
993 options.headerHeight = 0;
994 }
995
996 // Minimizable, dock is required and window cannot be modal
997 if (MochaUI.Dock && $(MochaUI.options.dock)){
998 if (MochaUI.Dock.dock && options.type != 'modal' && options.type != 'modal2'){
999 options.minimizable = options.minimizable;
1000 }
1001 }
1002 else {
1003 options.minimizable = false;
1004 }
1005
1006 // Maximizable, desktop is required
1007 options.maximizable = MochaUI.Desktop.desktop && options.maximizable && options.type != 'modal' && options.type != 'modal2';
1008
1009 if (this.options.type == 'modal2') {
1010 this.options.shadowBlur = 0;
1011 this.options.shadowOffset = {'x': 0, 'y': 0};
1012 this.options.useSpinner = false;
1013 this.options.useCanvas = false;
1014 this.options.footerHeight = 0;
1015 this.options.headerHeight = 0;
1016 }
1017
1018 // If window has no ID, give it one.
1019 if (options.id == null){
1020 options.id = 'win' + (++MochaUI.Windows.windowIDCount);
1021 }
1022 this.windowEl = $(options.id);
1023
1024 this.newWindow();
1025
1026 // Return window object
1027 return this;
1028 },
1029 saveValues: function(){
1030 var coordinates = this.windowEl.getCoordinates();
1031 this.options.x = coordinates.left.toInt();
1032 this.options.y = coordinates.top.toInt();
1033 },
1034 /*
1035
1036 Internal Function: newWindow
1037
1038 Arguments:
1039 properties
1040
1041 */
1042 newWindow: function(properties){ // options is not doing anything
1043
1044 // Shorten object chain
1045 var instances = MochaUI.Windows.instances;
1046 var instanceID = instances.get(this.options.id);
1047
1048 // Here we check to see if there is already a class instance for this window
1049 if (instanceID){
1050 var currentInstance = instanceID;
1051 }
1052
1053 // Check if window already exists and is not in progress of closing
1054 if ( this.windowEl && !this.isClosing ){
1055 // Restore if minimized
1056 if (currentInstance.isMinimized){
1057 MochaUI.Dock.restoreMinimized(this.windowEl);
1058 }
1059 // Expand and focus if collapsed
1060 if (currentInstance.isCollapsed){
1061 MochaUI.collapseToggle(this.windowEl);
1062 setTimeout(MochaUI.focusWindow.pass(this.windowEl, this),10);
1063 }
1064 // Else focus
1065 else {
1066 var coordinates = document.getCoordinates();
1067 if (this.windowEl.getStyle('left').toInt() > coordinates.width || this.windowEl.getStyle('top').toInt() > coordinates.height){
1068 MochaUI.centerWindow(this.windowEl);
1069 }
1070 setTimeout(MochaUI.focusWindow.pass(this.windowEl, this),10);
1071 }
1072 return;
1073 }
1074 else {
1075 instances.set(this.options.id, this);
1076 }
1077
1078 this.isClosing = false;
1079 this.fireEvent('onBeforeBuild');
1080
1081 // Create window div
1082 MochaUI.Windows.indexLevel++;
1083 this.windowEl = new Element('div', {
1084 'class': 'mocha',
1085 'id': this.options.id,
1086 'styles': {
1087 'width': this.options.width,
1088 'height': this.options.height,
1089 'display': 'block',
1090 'opacity': 0,
1091 'zIndex': MochaUI.Windows.indexLevel += 2
1092 }
1093 });
1094
1095 this.windowEl.addClass(this.options.addClass);
1096
1097 if (this.options.type == 'modal2') {
1098 this.windowEl.addClass('modal2');
1099 }
1100
1101 // Fix a mouseover issue with gauges in IE7
1102 if ( Browser.Engine.trident && this.options.shape == 'gauge') {
1103 this.windowEl.setStyle('background', 'url(../images/spacer.gif)');
1104 }
1105
1106 if ((this.options.type == 'modal' || this.options.type == 'modal2' ) && Browser.Platform.mac && Browser.Engine.gecko){
1107 if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)) {
1108 var ffversion = new Number(RegExp.$1);
1109 if (ffversion < 3) {
1110 this.windowEl.setStyle('position', 'fixed');
1111 }
1112 }
1113 }
1114
1115 if (this.options.loadMethod == 'iframe') {
1116 this.options.padding = { top: 0, right: 0, bottom: 0, left: 0 };
1117 }
1118
1119 // Insert sub elements inside windowEl
1120 this.insertWindowElements();
1121
1122 // Set title
1123 this.titleEl.set('html',this.options.title);
1124
1125 // Set scrollbars, always use 'hidden' for iframe windows
1126 this.contentWrapperEl.setStyles({
1127 'overflow': 'hidden',
1128 'background': this.options.contentBgColor
1129 });
1130
1131 this.contentEl.setStyles({
1132 'padding-top': this.options.padding.top,
1133 'padding-bottom': this.options.padding.bottom,
1134 'padding-left': this.options.padding.left,
1135 'padding-right': this.options.padding.right
1136 });
1137
1138
1139 if (this.options.shape == 'gauge'){
1140 if (this.options.useCanvasControls){
1141 this.canvasControlsEl.setStyle('display', 'none');
1142 }
1143 else {
1144 this.controlsEl.setStyle('display', 'none');
1145 }
1146 this.windowEl.addEvent('mouseover', function(){
1147 this.mouseover = true;
1148 var showControls = function(){
1149 if (this.mouseover != false){
1150 if (this.options.useCanvasControls){
1151 this.canvasControlsEl.setStyle('display', 'block');
1152 }
1153 else {
1154 this.controlsEl.setStyle('display', 'block');
1155 }
1156 this.canvasHeaderEl.setStyle('display', 'block');
1157 this.titleEl.setStyle('display', 'block');
1158 }
1159 };
1160 showControls.delay(150, this);
1161
1162 }.bind(this));
1163 this.windowEl.addEvent('mouseleave', function(){
1164 this.mouseover = false;
1165 if (this.options.useCanvasControls){
1166 this.canvasControlsEl.setStyle('display', 'none');
1167 }
1168 else {
1169 this.controlsEl.setStyle('display', 'none');
1170 }
1171 this.canvasHeaderEl.setStyle('display', 'none');
1172 this.titleEl.setStyle('display', 'none');
1173 }.bind(this));
1174 }
1175
1176 // Inject window into DOM
1177 this.windowEl.injectInside(this.options.container);
1178
1179 if (this.options.type != 'notification'){
1180 this.setMochaControlsWidth();
1181 }
1182
1183 // Add content to window.
1184 MochaUI.updateContent({
1185 'element': this.windowEl,
1186 'content': this.options.content,
1187 'url': this.options.contentURL
1188 });
1189
1190 // Add content to window toolbar.
1191 if (this.options.toolbar == true){
1192 MochaUI.updateContent({
1193 'element': this.windowEl,
1194 'childElement': this.toolbarEl,
1195 'content': this.options.toolbarContent,
1196 'loadMethod': 'xhr',
1197 'url': this.options.toolbarURL
1198 });
1199 }
1200
1201 // Add content to window toolbar.
1202 if (this.options.toolbar2 == true){
1203 MochaUI.updateContent({
1204 'element': this.windowEl,
1205 'childElement': this.toolbar2El,
1206 'content': this.options.toolbar2Content,
1207 'loadMethod': 'xhr',
1208 'url': this.options.toolbar2URL
1209 });
1210 }
1211
1212 this.drawWindow(this.windowEl);
1213
1214 // Attach events to the window
1215 this.attachDraggable(this.windowEl);
1216 this.attachResizable(this.windowEl);
1217 this.setupEvents(this.windowEl);
1218
1219 if (this.options.resizable){
1220 this.adjustHandles();
1221 }
1222
1223 // Move window into position. If position not specified by user then center the window on the page.
1224 if (this.options.container == document.body || this.options.container == MochaUI.Desktop.desktop){
1225 var dimensions = window.getSize();
1226 }
1227 else {
1228 var dimensions = $(this.options.container).getSize();
1229 }
1230
1231 if (!this.options.y) {
1232 var y = (dimensions.y * .5) - ((this.options.height + this.headerFooterShadow + this.windowEl.getStyle('border-top').toInt() + this.windowEl.getStyle('border-bottom').toInt()) * .5);
1233 }
1234 else {
1235 var y = this.options.y - this.options.shadowBlur;
1236 }
1237
1238 if (!this.options.x) {
1239 var x = (dimensions.x * .5) - (this.options.width * .5);
1240 }
1241 else {
1242 var x = this.options.x - this.options.shadowBlur;
1243 }
1244
1245 this.windowEl.setStyles({
1246 'top': y,
1247 'left': x
1248 });
1249
1250 // Create opacityMorph
1251 if (MochaUI.options.useEffects == true){
1252 // IE cannot handle both element opacity and VML alpha at the same time.
1253 if (Browser.Engine.trident){
1254 this.drawWindow(this.windowEl, false);
1255 }
1256 this.opacityMorph = new Fx.Morph(this.windowEl, {
1257 'duration': 350,
1258 onComplete: function(){
1259 if (Browser.Engine.trident){
1260 this.drawWindow(this.windowEl);
1261 }
1262 }.bind(this)
1263 });
1264 }
1265
1266 if (this.options.type == 'modal' || this.options.type == 'modal2') {
1267 MochaUI.currentModal = this.windowEl;
1268 if (Browser.Engine.trident4){
1269 $('modalFix').setStyle('display', 'block');
1270 }
1271 $('modalOverlay').setStyle('display', 'block');
1272 if (MochaUI.options.useEffects == false){
1273 $('modalOverlay').setStyle('opacity', .6);
1274 this.windowEl.setStyles({
1275 'zIndex': 11000,
1276 'opacity': 1
1277 });
1278 }
1279 else {
1280 MochaUI.Modal.modalOverlayCloseMorph.cancel();
1281 MochaUI.Modal.modalOverlayOpenMorph.start({
1282 'opacity': .6
1283 });
1284 this.windowEl.setStyles({
1285 'zIndex': 11000
1286 });
1287 this.opacityMorph.start({
1288 'opacity': 1
1289 });
1290 }
1291
1292 $$('.dockTab').removeClass('activeDockTab');
1293 $$('.mocha').removeClass('isFocused');
1294 this.windowEl.addClass('isFocused');
1295
1296 }
1297 else if (MochaUI.options.useEffects == false){
1298 this.windowEl.setStyle('opacity', 1);
1299 setTimeout(MochaUI.focusWindow.pass(this.windowEl, this), 10);
1300 }
1301 else {
1302 this.opacityMorph.start({
1303 'opacity': 1
1304 });
1305 setTimeout(MochaUI.focusWindow.pass(this.windowEl, this), 10);
1306 }
1307
1308 // This is a generic morph that can be reused later by functions like centerWindow()
1309 this.morph = new Fx.Morph(this.windowEl, {
1310 'duration': 200
1311 });
1312
1313 // Add check mark to menu if link exists in menu
1314 // Need to make sure the check mark is not added to links not in menu
1315
1316 if ($(this.windowEl.id + 'LinkCheck')){
1317 this.check = new Element('div', {
1318 'class': 'check',
1319 'id': this.options.id + '_check'
1320 }).inject(this.windowEl.id + 'LinkCheck');
1321 }
1322
1323 if (this.options.closeAfter != false){
1324 MochaUI.closeWindow.delay(this.options.closeAfter, this, this.windowEl);
1325 }
1326
1327 if (MochaUI.Dock && $(MochaUI.options.dock) && this.options.type == 'window' ){
1328 MochaUI.Dock.createDockTab(this.windowEl);
1329 }
1330
1331 },
1332 setupEvents: function(windowEl) {
1333
1334 // Set events
1335 // Note: if a button does not exist, its due to properties passed to newWindow() stating otherwice
1336 if (this.closeButtonEl){
1337 this.closeButtonEl.addEvent('click', function(e) {
1338 new Event(e).stop();
1339 MochaUI.closeWindow(windowEl);
1340 }.bind(this));
1341 }
1342
1343 if (this.options.type == 'window'){
1344 windowEl.addEvent('mousedown', function() {
1345 MochaUI.focusWindow(windowEl);
1346 }.bind(this));
1347 }
1348
1349 if (this.minimizeButtonEl) {
1350 this.minimizeButtonEl.addEvent('click', function(e) {
1351 new Event(e).stop();
1352 MochaUI.Dock.minimizeWindow(windowEl);
1353 }.bind(this));
1354 }
1355
1356 if (this.maximizeButtonEl) {
1357 this.maximizeButtonEl.addEvent('click', function(e) {
1358 new Event(e).stop();
1359 if (this.isMaximized) {
1360 MochaUI.Desktop.restoreWindow(windowEl);
1361 } else {
1362 MochaUI.Desktop.maximizeWindow(windowEl);
1363 }
1364 }.bind(this));
1365 }
1366
1367 if (this.options.collapsible == true){
1368 // Keep titlebar text from being selected on double click in Safari.
1369 this.titleEl.addEvent('selectstart', function(e) {
1370 e = new Event(e).stop();
1371 }.bind(this));
1372 // Keep titlebar text from being selected on double click in Opera.
1373 this.titleBarEl.addEvent('mousedown', function(e) {
1374 if (Browser.Engine.trident) {
1375 this.titleEl.setCapture();
1376 }
1377 }.bind(this));
1378 this.titleBarEl.addEvent('mouseup', function(e) {
1379 if (Browser.Engine.trident) {
1380 this.titleEl.releaseCapture();
1381 }
1382 }.bind(this));
1383 this.titleBarEl.addEvent('dblclick', function(e) {
1384 e = new Event(e).stop();
1385 MochaUI.collapseToggle(this.windowEl);
1386 }.bind(this));
1387 }
1388
1389 },
1390 /*
1391
1392 Internal Function: attachDraggable()
1393 Make window draggable.
1394
1395 Arguments:
1396 windowEl
1397
1398 */
1399 attachDraggable: function(windowEl){
1400 if (!this.options.draggable) return;
1401 this.windowDrag = new Drag.Move(windowEl, {
1402 handle: this.titleBarEl,
1403 container: this.options.restrict == true ? $(this.options.container) : false,
1404 grid: this.options.draggableGrid,
1405 limit: this.options.draggableLimit,
1406 snap: this.options.draggableSnap,
1407 onStart: function() {
1408 if (this.options.type != 'modal' && this.options.type != 'modal2'){
1409 MochaUI.focusWindow(windowEl);
1410 $('windowUnderlay').setStyle('display','block');
1411 }
1412 if ( this.iframeEl )
1413 this.iframeEl.setStyle('visibility', 'hidden');
1414 }.bind(this),
1415 onComplete: function() {
1416 if (this.options.type != 'modal' && this.options.type != 'modal2') {
1417 $('windowUnderlay').setStyle('display', 'none');
1418 }
1419 if ( this.iframeEl ){
1420 this.iframeEl.setStyle('visibility', 'visible');
1421 }
1422 // Store new position in options.
1423 this.saveValues();
1424 }.bind(this)
1425 });
1426 },
1427 /*
1428
1429 Internal Function: attachResizable
1430 Make window resizable.
1431
1432 Arguments:
1433 windowEl
1434
1435 */
1436 attachResizable: function(windowEl){
1437 if (!this.options.resizable) return;
1438 this.resizable1 = this.windowEl.makeResizable({
1439 handle: [this.n, this.ne, this.nw],
1440 limit: {
1441 y: [
1442 function(){
1443 return this.windowEl.getStyle('top').toInt() + this.windowEl.getStyle('height').toInt() - this.options.resizeLimit.y[1];
1444 }.bind(this),
1445 function(){
1446 return this.windowEl.getStyle('top').toInt() + this.windowEl.getStyle('height').toInt() - this.options.resizeLimit.y[0];
1447 }.bind(this)
1448 ]
1449 },
1450 modifiers: {x: false, y: 'top'},
1451 onStart: function(){
1452 this.resizeOnStart();
1453 this.coords = this.contentWrapperEl.getCoordinates();
1454 this.y2 = this.coords.top.toInt() + this.contentWrapperEl.offsetHeight;
1455 }.bind(this),
1456 onDrag: function(){
1457 this.coords = this.contentWrapperEl.getCoordinates();
1458 this.contentWrapperEl.setStyle('height', this.y2 - this.coords.top.toInt());
1459 this.drawWindow(windowEl);
1460 this.adjustHandles();
1461 }.bind(this),
1462 onComplete: function(){
1463 this.resizeOnComplete();
1464 }.bind(this)
1465 });
1466
1467 this.resizable2 = this.contentWrapperEl.makeResizable({
1468 handle: [this.e, this.ne],
1469 limit: {
1470 x: [this.options.resizeLimit.x[0] - (this.options.shadowBlur * 2), this.options.resizeLimit.x[1] - (this.options.shadowBlur * 2) ]
1471 },
1472 modifiers: {x: 'width', y: false},
1473 onStart: function(){
1474 this.resizeOnStart();
1475 }.bind(this),
1476 onDrag: function(){
1477 this.drawWindow(windowEl);
1478 this.adjustHandles();
1479 }.bind(this),
1480 onComplete: function(){
1481 this.resizeOnComplete();
1482 }.bind(this)
1483 });
1484
1485 this.resizable3 = this.contentWrapperEl.makeResizable({
1486 container: this.options.restrict == true ? $(this.options.container) : false,
1487 handle: this.se,
1488 limit: {
1489 x: [this.options.resizeLimit.x[0] - (this.options.shadowBlur * 2), this.options.resizeLimit.x[1] - (this.options.shadowBlur * 2) ],
1490 y: [this.options.resizeLimit.y[0] - this.headerFooterShadow, this.options.resizeLimit.y[1] - this.headerFooterShadow]
1491 },
1492 modifiers: {x: 'width', y: 'height'},
1493 onStart: function(){
1494 this.resizeOnStart();
1495 }.bind(this),
1496 onDrag: function(){
1497 this.drawWindow(windowEl);
1498 this.adjustHandles();
1499 }.bind(this),
1500 onComplete: function(){
1501 this.resizeOnComplete();
1502 }.bind(this)
1503 });
1504
1505 this.resizable4 = this.contentWrapperEl.makeResizable({
1506 handle: [this.s, this.sw],
1507 limit: {
1508 y: [this.options.resizeLimit.y[0] - this.headerFooterShadow, this.options.resizeLimit.y[1] - this.headerFooterShadow]
1509 },
1510 modifiers: {x: false, y: 'height'},
1511 onStart: function(){
1512 this.resizeOnStart();
1513 }.bind(this),
1514 onDrag: function(){
1515 this.drawWindow(windowEl);
1516 this.adjustHandles();
1517 }.bind(this),
1518 onComplete: function(){
1519 this.resizeOnComplete();
1520 }.bind(this)
1521 });
1522
1523 this.resizable5 = this.windowEl.makeResizable({
1524 handle: [this.w, this.sw, this.nw],
1525 limit: {
1526 x: [
1527 function(){
1528 return this.windowEl.getStyle('left').toInt() + this.windowEl.getStyle('width').toInt() - this.options.resizeLimit.x[1];
1529 }.bind(this),
1530 function(){
1531 return this.windowEl.getStyle('left').toInt() + this.windowEl.getStyle('width').toInt() - this.options.resizeLimit.x[0];
1532 }.bind(this)
1533 ]
1534 },
1535 modifiers: {x: 'left', y: false},
1536 onStart: function(){
1537 this.resizeOnStart();
1538 this.coords = this.contentWrapperEl.getCoordinates();
1539 this.x2 = this.coords.left.toInt() + this.contentWrapperEl.offsetWidth;
1540 }.bind(this),
1541 onDrag: function(){
1542 this.coords = this.contentWrapperEl.getCoordinates();
1543 this.contentWrapperEl.setStyle('width', this.x2 - this.coords.left.toInt());
1544 this.drawWindow(windowEl);
1545 this.adjustHandles();
1546 }.bind(this),
1547 onComplete: function(){
1548 this.resizeOnComplete();
1549 }.bind(this)
1550 });
1551
1552 },
1553 resizeOnStart: function(){
1554 $('windowUnderlay').setStyle('display','block');
1555 if (this.iframeEl){
1556 this.iframeEl.setStyle('visibility', 'hidden');
1557 }
1558 },
1559 resizeOnComplete: function(){
1560 $('windowUnderlay').setStyle('display','none');
1561 if (this.iframeEl){
1562 this.iframeEl.setStyle('visibility', 'visible');
1563 }
1564 this.fireEvent('onResize', this.windowEl);
1565 },
1566 adjustHandles: function(){
1567
1568 var shadowBlur = this.options.shadowBlur;
1569 var shadowBlur2x = shadowBlur * 2;
1570 var shadowOffset = this.options.shadowOffset;
1571 var top = shadowBlur - shadowOffset.y - 1;
1572 var right = shadowBlur + shadowOffset.x - 1;
1573 var bottom = shadowBlur + shadowOffset.y - 1;
1574 var left = shadowBlur - shadowOffset.x - 1;
1575
1576 var coordinates = this.windowEl.getCoordinates();
1577 var width = coordinates.width - shadowBlur2x + 2;
1578 var height = coordinates.height - shadowBlur2x + 2;
1579
1580 this.n.setStyles({
1581 'top': top,
1582 'left': left + 10,
1583 'width': width - 20
1584 });
1585 this.e.setStyles({
1586 'top': top + 10,
1587 'right': right,
1588 'height': height - 30
1589 });
1590 this.s.setStyles({
1591 'bottom': bottom,
1592 'left': left + 10,
1593 'width': width - 30
1594 });
1595 this.w.setStyles({
1596 'top': top + 10,
1597 'left': left,
1598 'height': height - 20
1599 });
1600 this.ne.setStyles({
1601 'top': top,
1602 'right': right
1603 });
1604 this.se.setStyles({
1605 'bottom': bottom,
1606 'right': right
1607 });
1608 this.sw.setStyles({
1609 'bottom': bottom,
1610 'left': left
1611 });
1612 this.nw.setStyles({
1613 'top': top,
1614 'left': left
1615 });
1616 },
1617 detachResizable: function(){
1618 this.resizable1.detach();
1619 this.resizable2.detach();
1620 this.resizable3.detach();
1621 this.resizable4.detach();
1622 this.resizable5.detach();
1623 this.windowEl.getElements('.handle').setStyle('display', 'none');
1624 },
1625 reattachResizable: function(){
1626 this.resizable1.attach();
1627 this.resizable2.attach();
1628 this.resizable3.attach();
1629 this.resizable4.attach();
1630 this.resizable5.attach();
1631 this.windowEl.getElements('.handle').setStyle('display', 'block');
1632 },
1633 /*
1634
1635 Internal Function: insertWindowElements
1636
1637 Arguments:
1638 windowEl
1639
1640 */
1641 insertWindowElements: function(){
1642
1643 var options = this.options;
1644 var height = options.height;
1645 var width = options.width;
1646 var id = options.id;
1647
1648 var cache = {};
1649
1650 if (Browser.Engine.trident4){
1651 cache.zIndexFixEl = new Element('iframe', {
1652 'id': id + '_zIndexFix',
1653 'class': 'zIndexFix',
1654 'scrolling': 'no',
1655 'marginWidth': 0,
1656 'marginHeight': 0,
1657 'src': ''
1658 }).inject(this.windowEl);
1659 }
1660
1661 cache.overlayEl = new Element('div', {
1662 'id': id + '_overlay',
1663 'class': 'mochaOverlay'
1664 }).inject(this.windowEl);
1665
1666 cache.titleBarEl = new Element('div', {
1667 'id': id + '_titleBar',
1668 'class': 'mochaTitlebar',
1669 'styles': {
1670 'cursor': options.draggable ? 'move' : 'default'
1671 }
1672 }).inject(cache.overlayEl, 'top');
1673
1674 cache.titleEl = new Element('h3', {
1675 'id': id + '_title',
1676 'class': 'mochaTitle'
1677 }).inject(cache.titleBarEl);
1678
1679 if (options.icon != false){
1680 cache.titleBarEl.setStyles({
1681 'padding-left': 15,
1682 'background': 'url(' + options.icon + ') 5px 5px no-repeat'
1683 });
1684 }
1685
1686 cache.contentBorderEl = new Element('div', {
1687 'id': id + '_contentBorder',
1688 'class': 'mochaContentBorder'
1689 }).inject(cache.overlayEl);
1690
1691 if (options.toolbar){
1692 cache.toolbarWrapperEl = new Element('div', {
1693 'id': id + '_toolbarWrapper',
1694 'class': 'mochaToolbarWrapper'
1695 }).inject(cache.contentBorderEl, options.toolbarPosition == 'bottom' ? 'after' : 'before');
1696
1697 if (options.toolbarPosition == 'bottom') {
1698 cache.toolbarWrapperEl.addClass('bottom');
1699 }
1700 cache.toolbarEl = new Element('div', {
1701 'id': id + '_toolbar',
1702 'class': 'mochaToolbar'
1703 }).inject(cache.toolbarWrapperEl);
1704 }
1705
1706 if (options.toolbar2){
1707 cache.toolbar2WrapperEl = new Element('div', {
1708 'id': id + '_toolbar2Wrapper',
1709 'class': 'mochaToolbarWrapper'
1710 }).inject(cache.contentBorderEl, options.toolbar2Position == 'bottom' ? 'after' : 'before');
1711
1712 if (options.toolbar2Position == 'bottom') {
1713 cache.toolbar2WrapperEl.addClass('bottom');
1714 }
1715 cache.toolbar2El = new Element('div', {
1716 'id': id + '_toolbar2',
1717 'class': 'mochaToolbar'
1718 }).inject(cache.toolbar2WrapperEl);
1719 }
1720
1721 cache.contentWrapperEl = new Element('div', {
1722 'id': id + '_contentWrapper',
1723 'class': 'mochaContentWrapper',
1724 'styles': {
1725 'width': width + 'px',
1726 'height': height + 'px'
1727 }
1728 }).inject(cache.contentBorderEl);
1729
1730 if (this.options.shape == 'gauge'){
1731 cache.contentBorderEl.setStyle('borderWidth', 0);
1732 }
1733
1734 cache.contentEl = new Element('div', {
1735 'id': id + '_content',
1736 'class': 'mochaContent'
1737 }).inject(cache.contentWrapperEl);
1738
1739 if (this.options.useCanvas == true) {
1740 cache.canvasEl = new Element('canvas', {
1741 'id': id + '_canvas',
1742 'class': 'mochaCanvas',
1743 'width': 1,
1744 'height': 1
1745 }).inject(this.windowEl);
1746
1747 if (Browser.Engine.trident && MochaUI.ieSupport == 'excanvas'){
1748 G_vmlCanvasManager.initElement(cache.canvasEl);
1749 cache.canvasEl = this.windowEl.getElement('.mochaCanvas');
1750 }
1751 }
1752
1753 cache.controlsEl = new Element('div', {
1754 'id': id + '_controls',
1755 'class': 'mochaControls'
1756 }).inject(cache.overlayEl, 'after');
1757
1758 if (options.useCanvasControls == true){
1759 cache.canvasControlsEl = new Element('canvas', {
1760 'id': id + '_canvasControls',
1761 'class': 'mochaCanvasControls',
1762 'width': 14,
1763 'height': 14
1764 }).inject(this.windowEl);
1765
1766 if (Browser.Engine.trident && MochaUI.ieSupport == 'excanvas'){
1767 G_vmlCanvasManager.initElement(cache.canvasControlsEl);
1768 cache.canvasControlsEl = this.windowEl.getElement('.mochaCanvasControls');
1769 }
1770 }
1771
1772 if (options.closable){
1773 cache.closeButtonEl = new Element('div', {
1774 'id': id + '_closeButton',
1775 'class': 'mochaCloseButton',
1776 'title': 'Close'
1777 }).inject(cache.controlsEl);
1778 if (options.useCanvasControls == true){
1779 cache.closeButtonEl.setStyle('background', 'none');
1780 }
1781 }
1782
1783 if (options.maximizable){
1784 cache.maximizeButtonEl = new Element('div', {
1785 'id': id + '_maximizeButton',
1786 'class': 'mochaMaximizeButton',
1787 'title': 'Maximize'
1788 }).inject(cache.controlsEl);
1789 if (options.useCanvasControls == true){
1790 cache.maximizeButtonEl.setStyle('background', 'none');
1791 }
1792 }
1793
1794 if (options.minimizable){
1795 cache.minimizeButtonEl = new Element('div', {
1796 'id': id + '_minimizeButton',
1797 'class': 'mochaMinimizeButton',
1798 'title': 'Minimize'
1799 }).inject(cache.controlsEl);
1800 if (options.useCanvasControls == true){
1801 cache.minimizeButtonEl.setStyle('background', 'none');
1802 }
1803 }
1804
1805 if (options.useSpinner == true && options.shape != 'gauge' && options.type != 'notification'){
1806 cache.spinnerEl = new Element('div', {
1807 'id': id + '_spinner',
1808 'class': 'mochaSpinner',
1809 'width': 16,
1810 'height': 16
1811 }).inject(this.windowEl, 'bottom');
1812 }
1813
1814 if (this.options.shape == 'gauge'){
1815 cache.canvasHeaderEl = new Element('canvas', {
1816 'id': id + '_canvasHeader',
1817 'class': 'mochaCanvasHeader',
1818 'width': this.options.width,
1819 'height': 26
1820 }).inject(this.windowEl, 'bottom');
1821
1822 if (Browser.Engine.trident && MochaUI.ieSupport == 'excanvas'){
1823 G_vmlCanvasManager.initElement(cache.canvasHeaderEl);
1824 cache.canvasHeaderEl = this.windowEl.getElement('.mochaCanvasHeader');
1825 }
1826 }
1827
1828 if ( Browser.Engine.trident ){
1829 cache.overlayEl.setStyle('zIndex', 2);
1830 }
1831
1832 // For Mac Firefox 2 to help reduce scrollbar bugs in that browser
1833 if (Browser.Platform.mac && Browser.Engine.gecko){
1834 if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)){
1835 var ffversion = new Number(RegExp.$1);
1836 if (ffversion < 3){
1837 cache.overlayEl.setStyle('overflow', 'auto');
1838 }
1839 }
1840 }
1841
1842 if (options.resizable){
1843 cache.n = new Element('div', {
1844 'id': id + '_resizeHandle_n',
1845 'class': 'handle',
1846 'styles': {
1847 'top': 0,
1848 'left': 10,
1849 'cursor': 'n-resize'
1850 }
1851 }).inject(cache.overlayEl, 'after');
1852
1853 cache.ne = new Element('div', {
1854 'id': id + '_resizeHandle_ne',
1855 'class': 'handle corner',
1856 'styles': {
1857 'top': 0,
1858 'right': 0,
1859 'cursor': 'ne-resize'
1860 }
1861 }).inject(cache.overlayEl, 'after');
1862
1863 cache.e = new Element('div', {
1864 'id': id + '_resizeHandle_e',
1865 'class': 'handle',
1866 'styles': {
1867 'top': 10,
1868 'right': 0,
1869 'cursor': 'e-resize'
1870 }
1871 }).inject(cache.overlayEl, 'after');
1872
1873 cache.se = new Element('div', {
1874 'id': id + '_resizeHandle_se',
1875 'class': 'handle cornerSE',
1876 'styles': {
1877 'bottom': 0,
1878 'right': 0,
1879 'cursor': 'se-resize'
1880 }
1881 }).inject(cache.overlayEl, 'after');
1882
1883 cache.s = new Element('div', {
1884 'id': id + '_resizeHandle_s',
1885 'class': 'handle',
1886 'styles': {
1887 'bottom': 0,
1888 'left': 10,
1889 'cursor': 's-resize'
1890 }
1891 }).inject(cache.overlayEl, 'after');
1892
1893 cache.sw = new Element('div', {
1894 'id': id + '_resizeHandle_sw',
1895 'class': 'handle corner',
1896 'styles': {
1897 'bottom': 0,
1898 'left': 0,
1899 'cursor': 'sw-resize'
1900 }
1901 }).inject(cache.overlayEl, 'after');
1902
1903 cache.w = new Element('div', {
1904 'id': id + '_resizeHandle_w',
1905 'class': 'handle',
1906 'styles': {
1907 'top': 10,
1908 'left': 0,
1909 'cursor': 'w-resize'
1910 }
1911 }).inject(cache.overlayEl, 'after');
1912
1913 cache.nw = new Element('div', {
1914 'id': id + '_resizeHandle_nw',
1915 'class': 'handle corner',
1916 'styles': {
1917 'top': 0,
1918 'left': 0,
1919 'cursor': 'nw-resize'
1920 }
1921 }).inject(cache.overlayEl, 'after');
1922 }
1923 $extend(this, cache);
1924
1925 },
1926 /*
1927
1928 Internal function: drawWindow
1929 This is where we create the canvas GUI
1930
1931 Arguments:
1932 windowEl: the $(window)
1933 shadows: (boolean) false will draw a window without shadows
1934
1935 */
1936 drawWindow: function(windowEl, shadows) {
1937
1938 if (this.isCollapsed){
1939 this.drawWindowCollapsed(windowEl, shadows);
1940 return;
1941 }
1942
1943 var options = this.options;
1944 var shadowBlur = options.shadowBlur;
1945 var shadowBlur2x = shadowBlur * 2;
1946 var shadowOffset = this.options.shadowOffset;
1947
1948 this.overlayEl.setStyles({
1949 'width': this.contentWrapperEl.offsetWidth
1950 });
1951
1952 // Resize iframe when window is resized
1953 if (this.iframeEl) {
1954 this.iframeEl.setStyles({
1955 'height': this.contentWrapperEl.offsetHeight
1956 });
1957 }
1958
1959 var borderHeight = this.contentBorderEl.getStyle('border-top').toInt() + this.contentBorderEl.getStyle('border-bottom').toInt();
1960 var toolbarHeight = this.toolbarWrapperEl ? this.toolbarWrapperEl.getStyle('height').toInt() + this.toolbarWrapperEl.getStyle('border-top').toInt() : 0;
1961 var toolbar2Height = this.toolbar2WrapperEl ? this.toolbar2WrapperEl.getStyle('height').toInt() + this.toolbar2WrapperEl.getStyle('border-top').toInt() : 0;
1962
1963 this.headerFooterShadow = options.headerHeight + options.footerHeight + shadowBlur2x;
1964 var height = this.contentWrapperEl.getStyle('height').toInt() + this.headerFooterShadow + toolbarHeight + toolbar2Height + borderHeight;
1965 var width = this.contentWrapperEl.getStyle('width').toInt() + shadowBlur2x;
1966 this.windowEl.setStyles({
1967 'height': height,
1968 'width': width
1969 });
1970
1971 this.overlayEl.setStyles({
1972 'height': height,
1973 'top': shadowBlur - shadowOffset.y,
1974 'left': shadowBlur - shadowOffset.x
1975 });
1976
1977 // Opera requires the canvas height and width be set this way when resizing:
1978 if (this.options.useCanvas == true) {
1979 this.canvasEl.height = height;
1980 this.canvasEl.width = width;
1981 }
1982
1983 // Part of the fix for IE6 select z-index bug
1984 if (Browser.Engine.trident4){
1985 this.zIndexFixEl.setStyles({
1986 'width': width,
1987 'height': height
1988 })
1989 }
1990
1991 this.titleBarEl.setStyles({
1992 'width': width - shadowBlur2x,
1993 'height': options.headerHeight
1994 });
1995
1996 // Make sure loading icon is placed correctly.
1997 if (options.useSpinner == true && options.shape != 'gauge' && options.type != 'notification'){
1998 this.spinnerEl.setStyles({
1999 'left': shadowBlur - shadowOffset.x + 3,
2000 'bottom': shadowBlur + shadowOffset.y + 4
2001 });
2002 }
2003
2004 if (this.options.useCanvas != false) {
2005
2006 // Draw Window
2007 var ctx = this.canvasEl.getContext('2d');
2008 ctx.clearRect(0, 0, width, height);
2009
2010 switch (options.shape) {
2011 case 'box':
2012 this.drawBox(ctx, width, height, shadowBlur, shadowOffset, shadows);
2013 break;
2014 case 'gauge':
2015 this.drawGauge(ctx, width, height, shadowBlur, shadowOffset, shadows);
2016 break;
2017 }
2018
2019
2020 if (options.resizable){
2021 MochaUI.triangle(
2022 ctx,
2023 width - (shadowBlur + shadowOffset.x + 17),
2024 height - (shadowBlur + shadowOffset.y + 18),
2025 11,
2026 11,
2027 options.resizableColor,
2028 1.0
2029 );
2030 }
2031
2032 // Invisible dummy object. The last element drawn is not rendered consistently while resizing in IE6 and IE7
2033 if (Browser.Engine.trident){
2034 MochaUI.triangle(ctx, 0, 0, 10, 10, options.resizableColor, 0);
2035 }
2036 }
2037
2038 if (options.type != 'notification' && options.useCanvasControls == true){
2039 this.drawControls(width, height, shadows);
2040 }
2041
2042 },
2043 drawWindowCollapsed: function(windowEl, shadows) {
2044
2045 var options = this.options;
2046 var shadowBlur = options.shadowBlur;
2047 var shadowBlur2x = shadowBlur * 2;
2048 var shadowOffset = options.shadowOffset;
2049
2050 var headerShadow = options.headerHeight + shadowBlur2x + 2;
2051 var height = headerShadow;
2052 var width = this.contentWrapperEl.getStyle('width').toInt() + shadowBlur2x;
2053 this.windowEl.setStyle('height', height);
2054
2055 this.overlayEl.setStyles({
2056 'height': height,
2057 'top': shadowBlur - shadowOffset.y,
2058 'left': shadowBlur - shadowOffset.x
2059 });
2060
2061 // Opera height and width must be set like this, when resizing:
2062 this.canvasEl.height = height;
2063 this.canvasEl.width = width;
2064
2065 // Part of the fix for IE6 select z-index bug
2066 if (Browser.Engine.trident4){
2067 this.zIndexFixEl.setStyles({
2068 'width': width,
2069 'height': height
2070 });
2071 }
2072
2073 // Set width
2074 this.windowEl.setStyle('width', width);
2075 this.overlayEl.setStyle('width', width);
2076 this.titleBarEl.setStyles({
2077 'width': width - shadowBlur2x,
2078 'height': options.headerHeight
2079 });
2080
2081 // Draw Window
2082 if (this.options.useCanvas != false) {
2083 var ctx = this.canvasEl.getContext('2d');
2084 ctx.clearRect(0, 0, width, height);
2085
2086 this.drawBoxCollapsed(ctx, width, height, shadowBlur, shadowOffset, shadows);
2087 if (options.useCanvasControls == true) {
2088 this.drawControls(width, height, shadows);
2089 }
2090
2091 // Invisible dummy object. The last element drawn is not rendered consistently while resizing in IE6 and IE7
2092 if (Browser.Engine.trident){
2093 MochaUI.triangle(ctx, 0, 0, 10, 10, options.resizableColor, 0);
2094 }
2095 }
2096
2097 },
2098 drawControls : function(width, height, shadows){
2099 var options = this.options;
2100 var shadowBlur = options.shadowBlur;
2101 var shadowOffset = options.shadowOffset;
2102 var controlsOffset = options.controlsOffset;
2103
2104 // Make sure controls are placed correctly.
2105 this.controlsEl.setStyles({
2106 'right': shadowBlur + shadowOffset.x + controlsOffset.right,
2107 'top': shadowBlur - shadowOffset.y + controlsOffset.top
2108 });
2109
2110 this.canvasControlsEl.setStyles({
2111 'right': shadowBlur + shadowOffset.x + controlsOffset.right,
2112 'top': shadowBlur - shadowOffset.y + controlsOffset.top
2113 });
2114
2115 // Calculate X position for controlbuttons
2116 //var mochaControlsWidth = 52;
2117 this.closebuttonX = options.closable ? this.mochaControlsWidth - 7 : this.mochaControlsWidth + 12;
2118 this.maximizebuttonX = this.closebuttonX - (options.maximizable ? 19 : 0);
2119 this.minimizebuttonX = this.maximizebuttonX - (options.minimizable ? 19 : 0);
2120
2121 var ctx2 = this.canvasControlsEl.getContext('2d');
2122 ctx2.clearRect(0, 0, 100, 100);
2123
2124 if (this.options.closable){
2125 this.closebutton(
2126 ctx2,
2127 this.closebuttonX,
2128 7,
2129 options.closeBgColor,
2130 1.0,
2131 options.closeColor,
2132 1.0
2133 );
2134 }
2135 if (this.options.maximizable){
2136 this.maximizebutton(
2137 ctx2,
2138 this.maximizebuttonX,
2139 7,
2140 options.maximizeBgColor,
2141 1.0,
2142 options.maximizeColor,
2143 1.0
2144 );
2145 }
2146 if (this.options.minimizable){
2147 this.minimizebutton(
2148 ctx2,
2149 this.minimizebuttonX,
2150 7,
2151 options.minimizeBgColor,
2152 1.0,
2153 options.minimizeColor,
2154 1.0
2155 );
2156 }
2157
2158 },
2159 drawBox: function(ctx, width, height, shadowBlur, shadowOffset, shadows){
2160
2161 var shadowBlur2x = shadowBlur * 2;
2162 var cornerRadius = this.options.cornerRadius;
2163
2164 // This is the drop shadow. It is created onion style.
2165 if ( shadows != false ) {
2166 for (var x = 0; x <= shadowBlur; x++){
2167 MochaUI.roundedRect(
2168 ctx,
2169 shadowOffset.x + x,
2170 shadowOffset.y + x,
2171 width - (x * 2) - shadowOffset.x,
2172 height - (x * 2) - shadowOffset.y,
2173 cornerRadius + (shadowBlur - x),
2174 [0, 0, 0],
2175 x == shadowBlur ? .29 : .065 + (x * .01)
2176 );
2177 }
2178 }
2179 // Window body.
2180 this.bodyRoundedRect(
2181 ctx, // context
2182 shadowBlur - shadowOffset.x, // x
2183 shadowBlur - shadowOffset.y, // y
2184 width - shadowBlur2x, // width
2185 height - shadowBlur2x, // height
2186 cornerRadius, // corner radius
2187 this.options.bodyBgColor // Footer color
2188 );
2189
2190 if (this.options.type != 'notification'){
2191 // Window header.
2192 this.topRoundedRect(
2193 ctx, // context
2194 shadowBlur - shadowOffset.x, // x
2195 shadowBlur - shadowOffset.y, // y
2196 width - shadowBlur2x, // width
2197 this.options.headerHeight, // height
2198 cornerRadius, // corner radius
2199 this.options.headerStartColor, // Header gradient's top color
2200 this.options.headerStopColor // Header gradient's bottom color
2201 );
2202 }
2203 },
2204 drawBoxCollapsed: function(ctx, width, height, shadowBlur, shadowOffset, shadows){
2205
2206 var options = this.options;
2207 var shadowBlur2x = shadowBlur * 2;
2208 var cornerRadius = options.cornerRadius;
2209
2210 // This is the drop shadow. It is created onion style.
2211 if ( shadows != false ){
2212 for (var x = 0; x <= shadowBlur; x++){
2213 MochaUI.roundedRect(
2214 ctx,
2215 shadowOffset.x + x,
2216 shadowOffset.y + x,
2217 width - (x * 2) - shadowOffset.x,
2218 height - (x * 2) - shadowOffset.y,
2219 cornerRadius + (shadowBlur - x),
2220 [0, 0, 0],
2221 x == shadowBlur ? .3 : .06 + (x * .01)
2222 );
2223 }
2224 }
2225
2226 // Window header
2227 this.topRoundedRect2(
2228 ctx, // context
2229 shadowBlur - shadowOffset.x, // x
2230 shadowBlur - shadowOffset.y, // y
2231 width - shadowBlur2x, // width
2232 options.headerHeight + 2, // height
2233 cornerRadius, // corner radius
2234 options.headerStartColor, // Header gradient's top color
2235 options.headerStopColor // Header gradient's bottom color
2236 );
2237
2238 },
2239 drawGauge: function(ctx, width, height, shadowBlur, shadowOffset, shadows){
2240 var options = this.options;
2241 var radius = (width * .5) - (shadowBlur) + 16;
2242 if (shadows != false) {
2243 for (var x = 0; x <= shadowBlur; x++){
2244 MochaUI.circle(
2245 ctx,
2246 width * .5 + shadowOffset.x,
2247 (height + options.headerHeight) * .5 + shadowOffset.x,
2248 (width *.5) - (x * 2) - shadowOffset.x,
2249 [0, 0, 0],
2250 x == shadowBlur ? .75 : .075 + (x * .04)
2251 );
2252 }
2253 }
2254 MochaUI.circle(
2255 ctx,
2256 width * .5 - shadowOffset.x,
2257 (height + options.headerHeight) * .5 - shadowOffset.y,
2258 (width *.5) - shadowBlur,
2259 options.bodyBgColor,
2260 1
2261 );
2262
2263 // Draw gauge header
2264 this.canvasHeaderEl.setStyles({
2265 'top': shadowBlur - shadowOffset.y,
2266 'left': shadowBlur - shadowOffset.x
2267 });
2268 var ctx = this.canvasHeaderEl.getContext('2d');
2269 ctx.clearRect(0, 0, width, 100);
2270 ctx.beginPath();
2271 ctx.lineWidth = 24;
2272 ctx.lineCap = 'round';
2273 ctx.moveTo(13, 13);
2274 ctx.lineTo(width - (shadowBlur*2) - 13, 13);
2275 ctx.strokeStyle = 'rgba(0, 0, 0, .65)';
2276 ctx.stroke();
2277 },
2278 bodyRoundedRect: function(ctx, x, y, width, height, radius, rgb){
2279 ctx.fillStyle = 'rgba(' + rgb.join(',') + ', 100)';
2280 ctx.beginPath();
2281 ctx.moveTo(x, y + radius);
2282 ctx.lineTo(x, y + height - radius);
2283 ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
2284 ctx.lineTo(x + width - radius, y + height);
2285 ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
2286 ctx.lineTo(x + width, y + radius);
2287 ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
2288 ctx.lineTo(x + radius, y);
2289 ctx.quadraticCurveTo(x, y, x, y + radius);
2290 ctx.fill();
2291
2292 },
2293 topRoundedRect: function(ctx, x, y, width, height, radius, headerStartColor, headerStopColor){
2294 var lingrad = ctx.createLinearGradient(0, 0, 0, height);
2295 lingrad.addColorStop(0, 'rgba(' + headerStartColor.join(',') + ', 1)');
2296 lingrad.addColorStop(1, 'rgba(' + headerStopColor.join(',') + ', 1)');
2297 ctx.fillStyle = lingrad;
2298 ctx.beginPath();
2299 ctx.moveTo(x, y);
2300 ctx.lineTo(x, y + height);
2301 ctx.lineTo(x + width, y + height);
2302 ctx.lineTo(x + width, y + radius);
2303 ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
2304 ctx.lineTo(x + radius, y);
2305 ctx.quadraticCurveTo(x, y, x, y + radius);
2306 ctx.fill();
2307 /*
2308 ctx.beginPath();
2309 ctx.strokeStyle = '#000';
2310 ctx.lineWidth = 1;
2311 ctx.moveTo(x, y + height + .5);
2312 ctx.lineTo(x + width, y + height + .5);
2313 ctx.stroke();
2314 */
2315
2316 },
2317 topRoundedRect2: function(ctx, x, y, width, height, radius, headerStartColor, headerStopColor){
2318 var lingrad = ctx.createLinearGradient(0, this.options.shadowBlur - 1, 0, height + this.options.shadowBlur + 3);
2319 lingrad.addColorStop(0, 'rgba(' + headerStartColor.join(',') + ', 1)');
2320 lingrad.addColorStop(1, 'rgba(' + headerStopColor.join(',') + ', 1)');
2321 ctx.fillStyle = lingrad;
2322 ctx.beginPath();
2323 ctx.moveTo(x, y + radius);
2324 ctx.lineTo(x, y + height - radius);
2325 ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
2326 ctx.lineTo(x + width - radius, y + height);
2327 ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
2328 ctx.lineTo(x + width, y + radius);
2329 ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
2330 ctx.lineTo(x + radius, y);
2331 ctx.quadraticCurveTo(x, y, x, y + radius);
2332 ctx.fill();
2333 },
2334 maximizebutton: function(ctx, x, y, rgbBg, aBg, rgb, a){
2335 // Circle
2336 ctx.beginPath();
2337 ctx.moveTo(x, y);
2338 ctx.arc(x, y, 7, 0, Math.PI*2, true);
2339 ctx.fillStyle = 'rgba(' + rgbBg.join(',') + ',' + aBg + ')';
2340 ctx.fill();
2341 // X sign
2342 ctx.strokeStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
2343 ctx.beginPath();
2344 ctx.moveTo(x, y - 4);
2345 ctx.lineTo(x, y + 4);
2346 ctx.stroke();
2347 ctx.beginPath();
2348 ctx.moveTo(x - 4, y);
2349 ctx.lineTo(x + 4, y);
2350 ctx.stroke();
2351 },
2352 closebutton: function(ctx, x, y, rgbBg, aBg, rgb, a){
2353 // Circle
2354 ctx.beginPath();
2355 ctx.moveTo(x, y);
2356 ctx.arc(x, y, 7, 0, Math.PI*2, true);
2357 ctx.fillStyle = 'rgba(' + rgbBg.join(',') + ',' + aBg + ')';
2358 ctx.fill();
2359 // Plus sign
2360 ctx.strokeStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
2361 ctx.beginPath();
2362 ctx.moveTo(x - 3, y - 3);
2363 ctx.lineTo(x + 3, y + 3);
2364 ctx.stroke();
2365 ctx.beginPath();
2366 ctx.moveTo(x + 3, y - 3);
2367 ctx.lineTo(x - 3, y + 3);
2368 ctx.stroke();
2369 },
2370 minimizebutton: function(ctx, x, y, rgbBg, aBg, rgb, a){
2371 // Circle
2372 ctx.beginPath();
2373 ctx.moveTo(x,y);
2374 ctx.arc(x,y,7,0,Math.PI*2,true);
2375 ctx.fillStyle = 'rgba(' + rgbBg.join(',') + ',' + aBg + ')';
2376 ctx.fill();
2377 // Minus sign
2378 ctx.strokeStyle = 'rgba(' + rgb.join(',') + ',' + a + ')';
2379 ctx.beginPath();
2380 ctx.moveTo(x - 4, y);
2381 ctx.lineTo(x + 4, y);
2382 ctx.stroke();
2383 },
2384 /*
2385
2386 Function: hideSpinner
2387 Hides the spinner.
2388
2389 */
2390 hideSpinner: function(spinner) {
2391 if ($(spinner)) $(spinner).setStyle('visibility', 'hidden');
2392 },
2393 /*
2394
2395 Function: showSpinner
2396 Shows the spinner.
2397
2398 */
2399 showSpinner: function(spinner){
2400 if (!this.options.useSpinner || this.options.shape == 'gauge' || this.options.type == 'notification') return;
2401 $(spinner).setStyles({
2402 'visibility': 'visible'
2403 });
2404 },
2405 setMochaControlsWidth: function(){
2406 this.mochaControlsWidth = 0;
2407 var options = this.options;
2408 if (options.minimizable){
2409 this.mochaControlsWidth += (this.minimizeButtonEl.getStyle('margin-left').toInt() + this.minimizeButtonEl.getStyle('width').toInt());
2410 }
2411 if (options.maximizable){
2412 this.mochaControlsWidth += (this.maximizeButtonEl.getStyle('margin-left').toInt() + this.maximizeButtonEl.getStyle('width').toInt());
2413 }
2414 if (options.closable){
2415 this.mochaControlsWidth += (this.closeButtonEl.getStyle('margin-left').toInt() + this.closeButtonEl.getStyle('width').toInt());
2416 }
2417 this.controlsEl.setStyle('width', this.mochaControlsWidth);
2418 if (options.useCanvasControls == true){
2419 this.canvasControlsEl.setProperty('width', this.mochaControlsWidth);
2420 }
2421 }
2422 });
2423 MochaUI.Window.implement(new Options, new Events);
2424 /*
2425
2426 Script: Modal.js
2427 Create modal dialog windows.
2428
2429 Copyright:
2430 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
2431
2432 License:
2433 MIT-style license.
2434
2435 Requires:
2436 Core.js, Window.js
2437
2438 See Also:
2439 <Window>
2440
2441 */
2442
2443 MochaUI.Modal = new Class({
2444
2445 Extends: MochaUI.Window,
2446
2447 Implements: [Events, Options],
2448
2449 initialize: function(options){
2450
2451 this.modalInitialize();
2452
2453 window.addEvent('resize', function(){
2454 this.setModalSize();
2455 }.bind(this));
2456
2457 },
2458 modalInitialize: function(){
2459 var modalOverlay = new Element('div', {
2460 'id': 'modalOverlay',
2461 'styles': {
2462 'height': document.getCoordinates().height,
2463 'opacity': .6
2464 }
2465 }).inject(document.body);
2466
2467 modalOverlay.addEvent('click', function(e){
2468 MochaUI.closeWindow(MochaUI.currentModal);
2469 });
2470
2471 if (Browser.Engine.trident4){
2472 var modalFix = new Element('iframe', {
2473 'id': 'modalFix',
2474 'scrolling': 'no',
2475 'marginWidth': 0,
2476 'marginHeight': 0,
2477 'src': '',
2478 'styles': {
2479 'height': document.getCoordinates().height
2480 }
2481 }).inject(document.body);
2482 }
2483
2484 this.modalOverlayOpenMorph = new Fx.Morph($('modalOverlay'), {
2485 'duration': 150
2486 });
2487 this.modalOverlayCloseMorph = new Fx.Morph($('modalOverlay'), {
2488 'duration': 150,
2489 onComplete: function(){
2490 $('modalOverlay').setStyle('display', 'none');
2491 if (Browser.Engine.trident4){
2492 $('modalFix').setStyle('display', 'none');
2493 }
2494 }.bind(this)
2495 });
2496 },
2497 setModalSize: function(){
2498 $('modalOverlay').setStyle('height', document.getCoordinates().height);
2499 if (Browser.Engine.trident4){
2500 $('modalFix').setStyle('height', document.getCoordinates().height);
2501 }
2502 }
2503 });
2504 MochaUI.Modal.implement(new Options, new Events);
2505 /*
2506
2507 Script: Windows-from-html.js
2508 Create windows from html markup in page.
2509
2510 Copyright:
2511 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
2512
2513 License:
2514 MIT-style license.
2515
2516 Requires:
2517 Core.js, Window.js
2518
2519 Example:
2520 HTML markup.
2521 (start code)
2522 <div class="mocha" id="mywindow" style="width:300px;height:255px;top:50px;left:350px">
2523 <h3 class="mochaTitle">My Window</h3>
2524 <p>My Window Content</p>
2525 </div>
2526 (end)
2527
2528 See Also:
2529 <Window>
2530
2531 */
2532
2533 MochaUI.extend({
2534 NewWindowsFromHTML: function(){
2535 $$('div.mocha').each(function(el) {
2536 // Get the window title and destroy that element, so it does not end up in window content
2537 if ( Browser.Engine.presto || Browser.Engine.trident5 ){
2538 el.setStyle('display','block'); // Required by Opera, and probably IE7
2539 }
2540 var title = el.getElement('h3.mochaTitle');
2541 var elDimensions = el.getStyles('height', 'width');
2542 var properties = {
2543 id: el.getProperty('id'),
2544 height: elDimensions.height.toInt(),
2545 width: elDimensions.width.toInt(),
2546 x: el.getStyle('left').toInt(),
2547 y: el.getStyle('top').toInt()
2548 };
2549 // If there is a title element, set title and destroy the element so it does not end up in window content
2550 if ( title ) {
2551 properties.title = title.innerHTML;
2552 title.destroy();
2553 }
2554
2555 // Get content and destroy the element
2556 properties.content = el.innerHTML;
2557 el.destroy();
2558
2559 // Create window
2560 new MochaUI.Window(properties, true);
2561 }.bind(this));
2562 }
2563 });
2564 /*
2565
2566 Script: Windows-from-json.js
2567 Create one or more windows from JSON data. You can define all the same properties as you can for new MochaUI.Window(). Undefined properties are set to their defaults.
2568
2569 Copyright:
2570 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
2571
2572 License:
2573 MIT-style license.
2574
2575 Syntax:
2576 (start code)
2577 MochaUI.newWindowsFromJSON(properties);
2578 (end)
2579
2580 Example:
2581 (start code)
2582 MochaUI.jsonWindows = function(){
2583 var url = 'data/json-windows-data.js';
2584 var request = new Request.JSON({
2585 url: url,
2586 method: 'get',
2587 onComplete: function(properties) {
2588 MochaUI.newWindowsFromJSON(properties.windows);
2589 }
2590 }).send();
2591 }
2592 (end)
2593
2594 Note:
2595 Windows created from JSON are not compatible with the current cookie based version
2596 of Save and Load Workspace.
2597
2598 See Also:
2599 <Window>
2600
2601 */
2602
2603 MochaUI.extend({
2604 newWindowsFromJSON: function(properties){
2605 properties.each(function(properties) {
2606 new MochaUI.Window(properties);
2607 }.bind(this));
2608 }
2609 });
2610 /*
2611
2612 Script: Arrange-cascade.js
2613 Cascade windows.
2614
2615 Copyright:
2616 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
2617
2618 License:
2619 MIT-style license.
2620
2621 Requires:
2622 Core.js, Window.js
2623
2624 Syntax:
2625 (start code)
2626 MochaUI.arrangeCascade();
2627 (end)
2628
2629 */
2630
2631 MochaUI.options.extend({
2632 viewportTopOffset: 30, // Use a negative number if neccessary to place first window where you want it
2633 viewportLeftOffset: 20,
2634 windowTopOffset: 50, // Initial vertical spacing of each window
2635 windowLeftOffset: 40 // Initial horizontal spacing of each window
2636 });
2637
2638 MochaUI.extend({
2639 arrangeCascade: function(){
2640 // See how much space we have to work with
2641 var coordinates = document.getCoordinates();
2642
2643 var openWindows = 0;
2644 MochaUI.Windows.instances.each(function(instance){
2645 if (!instance.isMinimized) openWindows ++;
2646 });
2647
2648 if ((this.options.windowTopOffset * (openWindows + 1)) >= (coordinates.height - this.options.viewportTopOffset)) {
2649 var topOffset = (coordinates.height - this.options.viewportTopOffset) / (openWindows + 1);
2650 }
2651 else {
2652 var topOffset = this.options.windowTopOffset;
2653 }
2654
2655 if ((this.options.windowLeftOffset * (openWindows + 1)) >= (coordinates.width - this.options.viewportLeftOffset - 20)) {
2656 var leftOffset = (coordinates.width - this.options.viewportLeftOffset - 20) / (openWindows + 1);
2657 }
2658 else {
2659 var leftOffset = this.options.windowLeftOffset;
2660 }
2661
2662 var x = this.options.viewportLeftOffset;
2663 var y = this.options.viewportTopOffset;
2664 $$('div.mocha').each(function(windowEl){
2665 var currentWindowClass = MochaUI.Windows.instances.get(windowEl.id);
2666 if (!currentWindowClass.isMinimized && !currentWindowClass.isMaximized){
2667 id = windowEl.id;
2668 MochaUI.focusWindow(windowEl);
2669 x += leftOffset;
2670 y += topOffset;
2671
2672 if (MochaUI.options.useEffects == false){
2673 windowEl.setStyles({
2674 'top': y,
2675 'left': x
2676 });
2677 }
2678 else {
2679 var cascadeMorph = new Fx.Morph(windowEl, {
2680 'duration': 550
2681 });
2682 cascadeMorph.start({
2683 'top': y,
2684 'left': x
2685 });
2686 }
2687 }
2688 }.bind(this));
2689 }
2690 });
2691 /*
2692
2693 Script: Arrange-tile.js
2694 Cascade windows.
2695
2696 Authors:
2697 Harry Roberts and Greg Houston
2698
2699 License:
2700 MIT-style license.
2701
2702 Requires:
2703 Core.js, Window.js
2704
2705 Syntax:
2706 (start code)
2707 MochaUI.arrangeTile();
2708 (end)
2709
2710 */
2711
2712 MochaUI.extend({
2713 arrangeTile: function(){
2714 var x = 10;
2715 var y = 10;
2716
2717 var instances = MochaUI.Windows.instances;
2718
2719 var windowsNum = 0;
2720
2721 instances.each(function(instance){
2722 if (!instance.isMinimized && !instance.isMaximized){
2723 windowsNum++;
2724 }
2725 });
2726
2727 var cols = 3;
2728 var rows = Math.ceil(windowsNum / cols);
2729
2730 var coordinates = document.getCoordinates();
2731
2732 var col_width = ((coordinates.width - this.options.viewportLeftOffset) / cols);
2733 var col_height = ((coordinates.height - this.options.viewportTopOffset) / rows);
2734
2735 var row = 0;
2736 var col = 0;
2737
2738 instances.each(function(instance){
2739 if (!instance.isMinimized && !instance.isMaximized){
2740
2741 var content = instance.contentWrapperEl;
2742 var content_coords = content.getCoordinates();
2743 var window_coords = instance.windowEl.getCoordinates();
2744
2745 // Calculate the amount of padding around the content window
2746 var padding_top = content_coords.top - window_coords.top;
2747 var padding_bottom = window_coords.height - content_coords.height - padding_top;
2748 var padding_left = content_coords.left - window_coords.left;
2749 var padding_right = window_coords.width - content_coords.width - padding_left;
2750
2751 /*
2752
2753 // This resizes the windows
2754 if (instance.options.shape != 'gauge' && instance.options.resizable == true){
2755 var width = (col_width - 3 - padding_left - padding_right);
2756 var height = (col_height - 3 - padding_top - padding_bottom);
2757
2758 if (width > instance.options.resizeLimit.x[0] && width < instance.options.resizeLimit.x[1]){
2759 content.setStyle('width', width);
2760 }
2761 if (height > instance.options.resizeLimit.y[0] && height < instance.options.resizeLimit.y[1]){
2762 content.setStyle('height', height);
2763 }
2764
2765 }*/
2766
2767 var left = (x + (col * col_width));
2768 var top = (y + (row * col_height));
2769
2770 instance.windowEl.setStyles({
2771 'left': left,
2772 'top': top
2773 });
2774
2775 instance.drawWindow(instance.windowEl);
2776
2777 MochaUI.focusWindow(instance.windowEl);
2778
2779 if (++col === cols) {
2780 row++;
2781 col = 0;
2782 }
2783 }
2784 }.bind(this));
2785 }
2786 });/*
2787
2788 Script: Tabs.js
2789 Functionality for window tabs.
2790
2791 Copyright:
2792 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
2793
2794 License:
2795 MIT-style license.
2796
2797 Requires:
2798 Core.js, Window.js (for tabbed windows) or Layout.js (for tabbed panels)
2799
2800 */
2801
2802 MochaUI.extend({
2803 /*
2804
2805 Function: initializeTabs
2806 Add click event to each list item that fires the selected function.
2807
2808 */
2809 initializeTabs: function(el){
2810 $(el).getElements('li').each(function(listitem){
2811 listitem.addEvent('click', function(e){
2812 MochaUI.selected(this, el);
2813 });
2814 });
2815 },
2816 /*
2817
2818 Function: selected
2819 Add "selected" class to current list item and remove it from sibling list items.
2820
2821 Syntax:
2822 (start code)
2823 selected(el, parent);
2824 (end)
2825
2826 Arguments:
2827 el - the list item
2828 parent - the ul
2829
2830 */
2831 selected: function(el, parent){
2832 $(parent).getChildren().each(function(listitem){
2833 listitem.removeClass('selected');
2834 });
2835 el.addClass('selected');
2836 }
2837 });
2838
2839 /*
2840
2841 Script: Layout.js
2842 Create web application layouts. Enables window maximize.
2843
2844 Copyright:
2845 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
2846
2847 License:
2848 MIT-style license.
2849
2850 Requires:
2851 Core.js, Window.js
2852
2853 */
2854
2855 MochaUI.Desktop = new Class({
2856
2857 Extends: MochaUI.Window,
2858
2859 Implements: [Events, Options],
2860
2861 options: {
2862 // Naming options:
2863 // If you change the IDs of the Mocha Desktop containers in your HTML, you need to change them here as well.
2864 desktop: 'desktop',
2865 desktopHeader: 'desktopHeader',
2866 desktopFooter: 'desktopFooter',
2867 desktopNavBar: 'desktopNavbar',
2868 pageWrapper: 'pageWrapper',
2869 page: 'page',
2870 desktopFooter: 'desktopFooterWrapper'
2871 },
2872 initialize: function(options){
2873 this.setOptions(options);
2874 this.desktop = $(this.options.desktop);
2875 this.desktopHeader = $(this.options.desktopHeader);
2876 this.desktopNavBar = $(this.options.desktopNavBar);
2877 this.pageWrapper = $(this.options.pageWrapper);
2878 this.page = $(this.options.page);
2879 this.desktopFooter = $(this.options.desktopFooter);
2880
2881 // This is run on dock initialize so no need to do it twice.
2882 if (!MochaUI.Dock.dockWrapper){
2883 this.setDesktopSize();
2884 }
2885 this.menuInitialize();
2886
2887 // Resize desktop, page wrapper, modal overlay, and maximized windows when browser window is resized
2888 window.addEvent('resize', function(e){
2889 this.onBrowserResize();
2890 }.bind(this));
2891 },
2892 menuInitialize: function(){
2893 // Fix for dropdown menus in IE6
2894 if (Browser.Engine.trident4 && this.desktopNavBar){
2895 this.desktopNavBar.getElements('li').each(function(element) {
2896 element.addEvent('mouseenter', function(){
2897 this.addClass('ieHover');
2898 });
2899 element.addEvent('mouseleave', function(){
2900 this.removeClass('ieHover');
2901 });
2902 });
2903 };
2904 },
2905 onBrowserResize: function(){
2906 this.setDesktopSize();
2907 // Resize maximized windows to fit new browser window size
2908 setTimeout( function(){
2909 MochaUI.Windows.instances.each(function(instance){
2910 if (instance.isMaximized){
2911
2912 // Hide iframe while resize for better performance
2913 if ( instance.iframeEl ){
2914 instance.iframeEl.setStyle('visibility', 'hidden');
2915 }
2916
2917 var coordinates = document.getCoordinates();
2918 var borderHeight = instance.contentBorderEl.getStyle('border-top').toInt() + instance.contentBorderEl.getStyle('border-bottom').toInt();
2919 var toolbarHeight = instance.toolbarWrapperEl ? instance.toolbarWrapperEl.getStyle('height').toInt() + instance.toolbarWrapperEl.getStyle('border-top').toInt() : 0;
2920 instance.contentWrapperEl.setStyles({
2921 'height': coordinates.height - instance.options.headerHeight - instance.options.footerHeight - borderHeight - toolbarHeight,
2922 'width': coordinates.width
2923 });
2924
2925 instance.drawWindow($(instance.options.id));
2926 if ( instance.iframeEl ){
2927 instance.iframeEl.setStyles({
2928 'height': instance.contentWrapperEl.getStyle('height')
2929 });
2930 instance.iframeEl.setStyle('visibility', 'visible');
2931 }
2932
2933 }
2934 }.bind(this));
2935 }.bind(this), 100);
2936 },
2937 setDesktopSize: function(){
2938 var windowDimensions = window.getCoordinates();
2939
2940 // var dock = $(MochaUI.options.dock);
2941 var dockWrapper = $(MochaUI.options.dockWrapper);
2942
2943 // Setting the desktop height may only be needed by IE7
2944 if (this.desktop){
2945 this.desktop.setStyle('height', windowDimensions.height);
2946 }
2947
2948 // Set pageWrapper height so the dock doesn't cover the pageWrapper scrollbars.
2949 if (this.pageWrapper) {
2950
2951 var dockOffset = MochaUI.dockVisible ? dockWrapper.offsetHeight : 0;
2952 var pageWrapperHeight = windowDimensions.height;
2953 pageWrapperHeight -= this.pageWrapper.getStyle('border-top').toInt();
2954 pageWrapperHeight -= this.pageWrapper.getStyle('border-bottom').toInt();
2955 if (this.desktopHeader){ pageWrapperHeight -= this.desktopHeader.offsetHeight; }
2956 if (this.desktopFooter){ pageWrapperHeight -= this.desktopFooter.offsetHeight; }
2957 pageWrapperHeight -= dockOffset;
2958
2959 if (pageWrapperHeight < 0){
2960 pageWrapperHeight = 0;
2961 }
2962 this.pageWrapper.setStyle('height', pageWrapperHeight);
2963 }
2964
2965 if (MochaUI.Columns.instances.getKeys().length > 0){ // Conditional is a fix for a bug in IE6 in the no toolbars demo.
2966 MochaUI.Desktop.resizePanels();
2967 }
2968 },
2969 resizePanels: function(){
2970 if (Browser.Engine.trident4){
2971 $$('.pad').setStyle('display', 'none');
2972 $$('.rHeight').setStyle('height', 1);
2973 }
2974 MochaUI.panelHeight();
2975 MochaUI.rWidth();
2976 if (Browser.Engine.trident4) $$('.pad').setStyle('display', 'block');
2977 },
2978 /*
2979
2980 Function: maximizeWindow
2981 Maximize a window.
2982
2983 Syntax:
2984 (start code)
2985 MochaUI.Desktop.maximizeWindow(windowEl);
2986 (end)
2987
2988 */
2989 maximizeWindow: function(windowEl){
2990
2991 var currentInstance = MochaUI.Windows.instances.get(windowEl.id);
2992 var options = currentInstance.options;
2993 var windowDrag = currentInstance.windowDrag;
2994
2995 // If window no longer exists or is maximized, stop
2996 if (windowEl != $(windowEl) || currentInstance.isMaximized ) return;
2997
2998 if (currentInstance.isCollapsed){
2999 MochaUI.collapseToggle(windowEl);
3000 }
3001
3002 currentInstance.isMaximized = true;
3003
3004 // If window is restricted to a container, it should not be draggable when maximized.
3005 if (currentInstance.options.restrict){
3006 windowDrag.detach();
3007 if (options.resizable) {
3008 currentInstance.detachResizable();
3009 }
3010 currentInstance.titleBarEl.setStyle('cursor', 'default');
3011 }
3012
3013 // If the window has a container that is not the desktop
3014 // temporarily move the window to the desktop while it is minimized.
3015 if (options.container != this.desktop){
3016 this.desktop.grab(windowEl);
3017 if (this.options.restrict){
3018 windowDrag.container = this.desktop;
3019 }
3020 }
3021
3022 // Save original position
3023 currentInstance.oldTop = windowEl.getStyle('top');
3024 currentInstance.oldLeft = windowEl.getStyle('left');
3025
3026 var contentWrapperEl = currentInstance.contentWrapperEl;
3027
3028 // Save original dimensions
3029 contentWrapperEl.oldWidth = contentWrapperEl.getStyle('width');
3030 contentWrapperEl.oldHeight = contentWrapperEl.getStyle('height');
3031
3032 // Hide iframe
3033 // Iframe should be hidden when minimizing, maximizing, and moving for performance and Flash issues
3034 if ( currentInstance.iframeEl ) {
3035 currentInstance.iframeEl.setStyle('visibility', 'hidden');
3036 }
3037
3038 var windowDimensions = document.getCoordinates();
3039 var options = currentInstance.options;
3040 var shadowBlur = options.shadowBlur;
3041 var shadowOffset = options.shadowOffset;
3042 var newHeight = windowDimensions.height - options.headerHeight - options.footerHeight;
3043 newHeight -= currentInstance.contentBorderEl.getStyle('border-top').toInt();
3044 newHeight -= currentInstance.contentBorderEl.getStyle('border-bottom').toInt();
3045 newHeight -= ( currentInstance.toolbarWrapperEl ? currentInstance.toolbarWrapperEl.getStyle('height').toInt() + currentInstance.toolbarWrapperEl.getStyle('border-top').toInt() : 0);
3046
3047 if (MochaUI.options.useEffects == false){
3048 windowEl.setStyles({
3049 'top': shadowOffset.y - shadowBlur,
3050 'left': shadowOffset.x - shadowBlur
3051 });
3052 currentInstance.contentWrapperEl.setStyles({
3053 'height': newHeight,
3054 'width': windowDimensions.width
3055 });
3056 currentInstance.drawWindow(windowEl);
3057 // Show iframe
3058 if ( currentInstance.iframeEl ) {
3059 currentInstance.iframeEl.setStyle('visibility', 'visible');
3060 }
3061 currentInstance.fireEvent('onMaximize', windowEl);
3062 }
3063 else {
3064
3065 // Todo: Initialize the variables for these morphs once in an initialize function and reuse them
3066
3067 var maximizeMorph = new Fx.Elements([contentWrapperEl, windowEl], {
3068 duration: 70,
3069 onStart: function(windowEl){
3070 currentInstance.maximizeAnimation = currentInstance.drawWindow.periodical(20, currentInstance, windowEl);
3071 }.bind(this),
3072 onComplete: function(windowEl){
3073 $clear(currentInstance.maximizeAnimation);
3074 currentInstance.drawWindow(windowEl);
3075 // Show iframe
3076 if ( currentInstance.iframeEl ) {
3077 currentInstance.iframeEl.setStyle('visibility', 'visible');
3078 }
3079 currentInstance.fireEvent('onMaximize', windowEl);
3080 }.bind(this)
3081 });
3082 maximizeMorph.start({
3083 '0': { 'height': newHeight,
3084 'width': windowDimensions.width
3085 },
3086 '1': { 'top': shadowOffset.y - shadowBlur,
3087 'left': shadowOffset.x - shadowBlur
3088 }
3089 });
3090 }
3091 currentInstance.maximizeButtonEl.setProperty('title', 'Restore');
3092 MochaUI.focusWindow(windowEl);
3093
3094 },
3095 /*
3096
3097 Function: restoreWindow
3098 Restore a maximized window.
3099
3100 Syntax:
3101 (start code)
3102 MochaUI.Desktop.restoreWindow(windowEl);
3103 (end)
3104
3105 */
3106 restoreWindow: function(windowEl){
3107
3108 var currentInstance = MochaUI.Windows.instances.get(windowEl.id);
3109
3110 // Window exists and is maximized ?
3111 if (windowEl != $(windowEl) || !currentInstance.isMaximized) return;
3112
3113 var options = currentInstance.options;
3114 currentInstance.isMaximized = false;
3115
3116 if (options.restrict){
3117 currentInstance.windowDrag.attach();
3118 if (options.resizable){
3119 currentInstance.reattachResizable();
3120 }
3121 currentInstance.titleBarEl.setStyle('cursor', 'move');
3122 }
3123
3124 // Hide iframe
3125 // Iframe should be hidden when minimizing, maximizing, and moving for performance and Flash issues
3126 if ( currentInstance.iframeEl ) {
3127 currentInstance.iframeEl.setStyle('visibility', 'hidden');
3128 }
3129
3130 var contentWrapperEl = currentInstance.contentWrapperEl;
3131
3132 if (MochaUI.options.useEffects == false){
3133 contentWrapperEl.setStyles({
3134 'width': contentWrapperEl.oldWidth,
3135 'height': contentWrapperEl.oldHeight
3136 });
3137 currentInstance.drawWindow(windowEl);
3138 windowEl.setStyles({
3139 'top': currentInstance.oldTop,
3140 'left': currentInstance.oldLeft
3141 });
3142 if ( currentInstance.iframeEl ) {
3143 currentInstance.iframeEl.setStyle('visibility', 'visible');
3144 }
3145 if (options.container != this.desktop){
3146 $(options.container).grab(windowEl);
3147 if (options.restrict){
3148 currentInstance.windowDrag.container = $(options.container);
3149 }
3150 }
3151 currentInstance.fireEvent('onRestore', windowEl);
3152 }
3153 else {
3154 var restoreMorph = new Fx.Elements([contentWrapperEl, windowEl], {
3155 'duration': 150,
3156 'onStart': function(windowEl){
3157 currentInstance.maximizeAnimation = currentInstance.drawWindow.periodical(20, currentInstance, windowEl);
3158 }.bind(this),
3159 'onComplete': function(el){
3160 $clear(currentInstance.maximizeAnimation);
3161 currentInstance.drawWindow(windowEl);
3162 if (currentInstance.iframeEl){
3163 currentInstance.iframeEl.setStyle('visibility', 'visible');
3164 }
3165 if (options.container != this.desktop){
3166 $(options.container).grab(windowEl);
3167 if (options.restrict){
3168 currentInstance.windowDrag.container = $(options.container);
3169 }
3170 }
3171 currentInstance.fireEvent('onRestore', windowEl);
3172 }.bind(this)
3173 });
3174 restoreMorph.start({
3175 '0': { 'height': contentWrapperEl.oldHeight,
3176 'width': contentWrapperEl.oldWidth
3177 },
3178 '1': { 'top': currentInstance.oldTop,
3179 'left': currentInstance.oldLeft
3180 }
3181 });
3182 }
3183 currentInstance.maximizeButtonEl.setProperty('title', 'Maximize');
3184 }
3185 });
3186 MochaUI.Desktop.implement(new Options, new Events);
3187
3188 /*
3189
3190 Class: Column
3191 Create a column. Columns should be created from left to right.
3192
3193 Syntax:
3194 (start code)
3195 MochaUI.Panel();
3196 (end)
3197
3198 Arguments:
3199 options
3200
3201 Options:
3202 id - The ID of the column. This must be set when creating the column.
3203 placement - Can be 'right', 'main', or 'left'. There must be at least one column with the 'main' option.
3204 width - 'main' column is fluid and should not be given a width.
3205 resizeLimit - resizelimit of a 'right' or 'left' column.
3206 onResize - (function) Fired when the column is resized.
3207 onCollapse - (function) Fired when the column is collapsed.
3208 onExpand - (function) Fired when the column is expanded.
3209
3210 */
3211 MochaUI.Column = new Class({
3212
3213 Extends: MochaUI.Desktop,
3214
3215 Implements: [Events, Options],
3216
3217 options: {
3218 id: null,
3219 placement: null,
3220 width: null,
3221 resizeLimit: [],
3222
3223 // Events
3224 onResize: $empty,
3225 onCollapse: $empty,
3226 onExpand: $empty
3227
3228 },
3229 initialize: function(options){
3230 this.setOptions(options);
3231
3232 $extend(this, {
3233 timestamp: $time(),
3234 isCollapsed: false,
3235 oldWidth: 0
3236 });
3237
3238 // Shorten object chain
3239 var options = this.options;
3240 var instances = MochaUI.Columns.instances;
3241 var instanceID = instances.get(options.id);
3242
3243 // Check to see if there is already a class instance for this Column
3244 if (instanceID){
3245 var currentInstance = instanceID;
3246 }
3247
3248 // Check if column already exists
3249 if ( this.columnEl ){
3250 return;
3251 }
3252 else {
3253 instances.set(options.id, this);
3254 }
3255
3256 this.columnEl = new Element('div', {
3257 'id': this.options.id,
3258 'class': 'column expanded',
3259 'styles': {
3260 'width': options.placement == 'main' ? null : options.width
3261 }
3262 }).inject($(MochaUI.Desktop.pageWrapper));
3263
3264 var parent = this.columnEl.getParent();
3265 var columnHeight = parent.getStyle('height').toInt();
3266 this.columnEl.setStyle('height', columnHeight);
3267
3268 if (options.placement == 'main'){
3269 this.columnEl.addClass('rWidth');
3270 }
3271
3272 this.spacerEl = new Element('div', {
3273 'id': this.options.id + '_spacer',
3274 'class': 'horizontalHandle'
3275 }).inject(this.columnEl);
3276
3277 switch (this.options.placement) {
3278 case 'left':
3279 this.handleEl = new Element('div', {
3280 'id': this.options.id + '_handle',
3281 'class': 'columnHandle'
3282 }).inject(this.columnEl, 'after');
3283
3284 this.handleIconEl = new Element('div', {
3285 'id': options.id + '_handle_icon',
3286 'class': 'handleIcon'
3287 }).inject(this.handleEl);
3288
3289 addResizeRight(this.columnEl, options.resizeLimit[0], options.resizeLimit[1]);
3290 break;
3291 case 'right':
3292 this.handleEl = new Element('div', {
3293 'id': this.options.id + '_handle',
3294 'class': 'columnHandle'
3295 }).inject(this.columnEl, 'before');
3296
3297 this.handleIconEl = new Element('div', {
3298 'id': options.id + '_handle_icon',
3299 'class': 'handleIcon'
3300 }).inject(this.handleEl);
3301 addResizeLeft(this.columnEl, options.resizeLimit[0], options.resizeLimit[1]);
3302 break;
3303 }
3304
3305 if (this.handleEl != null){
3306 this.handleEl.addEvent('dblclick', function(){
3307 this.columnToggle();
3308 }.bind(this));
3309 }
3310
3311 MochaUI.rWidth();
3312
3313 },
3314 columnToggle: function(){
3315 var column= this.columnEl;
3316
3317 // Collapse
3318 if (this.isCollapsed == false){
3319 this.oldWidth = column.getStyle('width').toInt();
3320
3321 this.resize.detach();
3322 this.handleEl.removeEvents('dblclick');
3323 this.handleEl.addEvent('click', function(){
3324 this.columnToggle();
3325 }.bind(this));
3326 this.handleEl.setStyle('cursor', 'pointer').addClass('detached');
3327
3328 column.setStyle('width', 0);
3329 this.isCollapsed = true;
3330 column.addClass('collapsed');
3331 column.removeClass('expanded');
3332
3333 MochaUI.rWidth();
3334 this.fireEvent('onCollapse');
3335 }
3336 // Expand
3337 else {
3338 column.setStyle('width', this.oldWidth);
3339 this.isCollapsed = false;
3340 column.addClass('expanded');
3341 column.removeClass('collapsed');
3342
3343 this.handleEl.removeEvents('click');
3344 this.handleEl.addEvent('dblclick', function(){
3345 this.columnToggle();
3346 }.bind(this));
3347 this.resize.attach();
3348 this.handleEl.setStyle('cursor', 'e-resize').addClass('attached');
3349
3350 MochaUI.rWidth();
3351 this.fireEvent('onExpand');
3352 }
3353 }
3354 });
3355 MochaUI.Column.implement(new Options, new Events);
3356
3357 /*
3358
3359 Class: Panel
3360 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.
3361
3362 Syntax:
3363 (start code)
3364 MochaUI.Panel();
3365 (end)
3366
3367 Arguments:
3368 options
3369
3370 Options:
3371 id - The ID of the panel. This must be set when creating the panel.
3372 column - Where to inject the panel. This must be set when creating the panel.
3373 loadMethod - ('html', 'xhr', or 'iframe')
3374 contentURL - Used if loadMethod is set to 'xhr' or 'iframe'.
3375 evalScripts - (boolean) An xhr loadMethod option. Defaults to true.
3376 evalResponse - (boolean) An xhr loadMethod option. Defaults to false.
3377 content - (string or element) An html loadMethod option.
3378 tabsURL - (url)
3379 footer - (boolean)
3380 footerURL - (url)
3381 height - (number) Height of content area.
3382 addClass - (string) Add a class to the panel.
3383 scrollbars - (boolean)
3384 padding - (object)
3385 panelBackground - CSS background property for the panel.
3386 onBeforeBuild - (function) Fired before the panel is created.
3387 onContentLoaded - (function) Fired after the panel's conten is loaded.
3388 onResize - (function) Fired when the panel is resized.
3389 onCollapse - (function) Fired when the panel is collapsed.
3390 onExpand - (function) Fired when the panel is expanded.
3391
3392 */
3393 MochaUI.Panel = new Class({
3394
3395 Extends: MochaUI.Desktop,
3396
3397 Implements: [Events, Options],
3398
3399 options: {
3400 id: null,
3401 title: 'New Panel',
3402 column: null,
3403 loadMethod: 'html',
3404 contentURL: 'pages/lipsum.html',
3405
3406 // xhr options
3407 evalScripts: true,
3408 evalResponse: false,
3409
3410 // html options
3411 content: 'Panel content',
3412
3413 // Tabs
3414 tabsURL: null,
3415
3416 footer: false,
3417 footerURL: 'pages/lipsum.html',
3418
3419 // Style options:
3420 height: 125,
3421 addClass: '',
3422 scrollbars: true,
3423 padding: { top: 8, right: 8, bottom: 8, left: 8 },
3424
3425 // Color options:
3426 panelBackground: '#f8f8f8',
3427
3428 // Events
3429 onBeforeBuild: $empty,
3430 onContentLoaded: $empty,
3431 onResize: $empty,
3432 onCollapse: $empty,
3433 onExpand: $empty
3434
3435 },
3436 initialize: function(options){
3437 this.setOptions(options);
3438
3439 $extend(this, {
3440 timestamp: $time(),
3441 isCollapsed: false,
3442 oldHeight: 0,
3443 partner: null
3444 });
3445
3446 // Shorten object chain
3447 var instances = MochaUI.Panels.instances;
3448 var instanceID = instances.get(this.options.id);
3449
3450 // Check to see if there is already a class instance for this panel
3451 if (instanceID){
3452 var currentInstance = instanceID;
3453 }
3454
3455 // Check if panel already exists
3456 if ( this.panelEl ){
3457 return;
3458 }
3459 else {
3460 instances.set(this.options.id, this);
3461 }
3462
3463 this.fireEvent('onBeforeBuild');
3464
3465 if (this.options.loadMethod == 'iframe') {
3466 // Iframes have their own scrollbars and padding.
3467 this.options.scrollbars = false;
3468 this.options.padding = { top: 0, right: 0, bottom: 0, left: 0 };
3469 }
3470
3471 this.showHandle = true;
3472 if ($(this.options.column).getChildren().length == 0){
3473 this.showHandle = false;
3474 }
3475
3476 this.panelEl = new Element('div', {
3477 'id': this.options.id,
3478 'class': 'panel expanded',
3479 'styles': {
3480 'height': this.options.height,
3481 'background': this.options.panelBackground
3482 }
3483 }).inject($(this.options.column));
3484
3485 this.panelEl.addClass(this.options.addClass);
3486
3487 this.contentEl = new Element('div', {
3488 'id': this.options.id + '_pad',
3489 'class': 'pad'
3490 }).inject(this.panelEl);
3491
3492 if (this.options.footer){
3493 this.footerWrapperEl = new Element('div', {
3494 'id': this.options.id + '_panelFooterWrapper',
3495 'class': 'panel-footerWrapper'
3496 }).inject(this.panelEl);
3497
3498 this.footerEl = new Element('div', {
3499 'id': this.options.id + '_panelFooter',
3500 'class': 'panel-footer'
3501 }).inject(this.footerWrapperEl);
3502
3503
3504 MochaUI.updateContent({
3505 'element': this.panelEl,
3506 'childElement': this.footerEl,
3507 'loadMethod': 'xhr',
3508 'url': this.options.footerURL
3509 });
3510
3511 }
3512
3513 // This is in order to use the same variable as the windows do in updateContent.
3514 // May rethink this.
3515 this.contentWrapperEl = this.panelEl;
3516
3517 // Set scrollbars, always use 'hidden' for iframe windows
3518 this.contentWrapperEl.setStyles({
3519 'overflow': this.options.scrollbars && !this.iframeEl ? 'auto' : 'hidden'
3520 });
3521
3522 this.contentEl.setStyles({
3523 'padding-top': this.options.padding.top,
3524 'padding-bottom': this.options.padding.bottom,
3525 'padding-left': this.options.padding.left,
3526 'padding-right': this.options.padding.right
3527 });
3528
3529 this.panelHeaderEl = new Element('div', {
3530 'id': this.options.id + '_header',
3531 'class': 'panel-header'
3532 }).inject(this.panelEl, 'before');
3533
3534 this.panelHeaderToolboxEl = new Element('div', {
3535 'id': this.options.id + '_headerToolbox',
3536 'class': 'panel-header-toolbox'
3537 }).inject(this.panelHeaderEl);
3538
3539 this.collapseToggleEl = new Element('div', {
3540 'id': this.options.id + '_minmize',
3541 'class': 'panel-collapse icon16',
3542 'styles': {
3543 'width': 16,
3544 'height': 16
3545 },
3546 'title': 'Collapse Panel'
3547 }).inject(this.panelHeaderToolboxEl);
3548
3549 this.collapseToggleEl.addEvent('click', function(event){
3550 var panel = this.panelEl;
3551
3552 // Get siblings and make sure they are not all collapsed.
3553 var instances = MochaUI.Panels.instances;
3554 var expandedSiblings = [];
3555 panel.getAllPrevious('.panel').each(function(sibling){
3556 var currentInstance = instances.get(sibling.id);
3557 if (currentInstance.isCollapsed == false){
3558 expandedSiblings.push(sibling);
3559 }
3560 });
3561 panel.getAllNext('.panel').each(function(sibling){
3562 var currentInstance = instances.get(sibling.id);
3563 if (currentInstance.isCollapsed == false){
3564 expandedSiblings.push(sibling);
3565 }
3566 });
3567
3568 if (this.isCollapsed == false) {
3569 var currentColumn = MochaUI.Columns.instances.get($(this.options.column).id);
3570
3571 if (expandedSiblings.length == 0 && currentColumn.options.placement != 'main'){
3572 var currentColumn = MochaUI.Columns.instances.get($(this.options.column).id);
3573 currentColumn.columnToggle();
3574 return;
3575 }
3576 else if (expandedSiblings.length == 0 && currentColumn.options.placement == 'main'){
3577 return;
3578 }
3579 this.oldHeight = panel.getStyle('height').toInt();
3580 if (this.oldHeight < 10) this.oldHeight = 20;
3581 panel.setStyle('height', 0);
3582 this.isCollapsed = true;
3583 panel.addClass('collapsed');
3584 panel.removeClass('expanded');
3585 MochaUI.panelHeight(this.options.column, panel, 'collapsing');
3586 this.collapseToggleEl.removeClass('panel-collapsed');
3587 this.collapseToggleEl.addClass('panel-expand');
3588 this.collapseToggleEl.setProperty('title','Expand Panel');
3589 this.fireEvent('onCollapse');
3590 }
3591 else {
3592 panel.setStyle('height', this.oldHeight);
3593 this.isCollapsed = false;
3594 panel.addClass('expanded');
3595 panel.removeClass('collapsed');
3596 MochaUI.panelHeight(this.options.column, panel, 'expanding');
3597 this.collapseToggleEl.removeClass('panel-expand');
3598 this.collapseToggleEl.addClass('panel-collapsed');
3599 this.collapseToggleEl.setProperty('title','Collapse Panel');
3600 this.fireEvent('onExpand');
3601 }
3602 }
3603 .bind(this));
3604
3605 this.panelHeaderContentEl = new Element('div', {
3606 'id': this.options.id + '_headerContent',
3607 'class': 'panel-headerContent'
3608 }).inject(this.panelHeaderEl);
3609
3610 this.titleEl = new Element('h2', {
3611 'id': this.options.id + '_title'
3612 }).inject(this.panelHeaderContentEl);
3613
3614 if (this.options.tabsURL == null){
3615 this.titleEl.set('html', this.options.title);
3616 }
3617 else {
3618 this.panelHeaderContentEl.addClass('tabs');
3619 MochaUI.updateContent({
3620 'element': this.panelEl,
3621 'childElement': this.panelHeaderContentEl,
3622 'loadMethod': 'xhr',
3623 'url': this.options.tabsURL
3624 });
3625 }
3626
3627 this.handleEl = new Element('div', {
3628 'id': this.options.id + '_handle',
3629 'class': 'horizontalHandle',
3630 'styles': {
3631 'display': this.showHandle == true ? 'block' : 'none'
3632 }
3633 }).inject(this.panelEl, 'after');
3634
3635 this.handleIconEl = new Element('div', {
3636 'id': this.options.id + '_handle_icon',
3637 'class': 'handleIcon'
3638 }).inject(this.handleEl);
3639
3640 addResizeBottom(this.options.id);
3641
3642 // Add content to panel.
3643 MochaUI.updateContent({
3644 'element': this.panelEl,
3645 'content': this.options.content,
3646 'url': this.options.contentURL
3647 });
3648
3649 MochaUI.panelHeight(this.options.column, this.panelEl, 'new');
3650
3651 }
3652 });
3653 MochaUI.Panel.implement(new Options, new Events);
3654
3655
3656 MochaUI.extend({
3657 // Panel Height
3658 panelHeight: function(column, changing, action){
3659 if (column != null) {
3660 MochaUI.panelHeight2($(column), changing, action);
3661 }
3662 else {
3663 $$('.column').each(function(column){
3664 MochaUI.panelHeight2(column);
3665 }.bind(this));
3666 }
3667 },
3668 /*
3669
3670 actions can be new, collapsing or expanding.
3671
3672 */
3673 panelHeight2: function(column, changing, action){
3674
3675 var instances = MochaUI.Panels.instances;
3676
3677 var parent = column.getParent();
3678 var columnHeight = parent.getStyle('height').toInt();
3679 if (Browser.Engine.trident4){
3680 columnHeight -= 1;
3681 }
3682 column.setStyle('height', columnHeight);
3683
3684 var panels = column.getChildren('.panel'); // All the panels in the column.
3685 var panelsExpanded = column.getChildren('.expanded'); // All the expanded panels in the column.
3686 var panelsToResize = []; // All the panels in the column whose height will be effected.
3687 var tallestPanel; // The panel with the greatest height
3688 var tallestPanelHeight = 0;
3689
3690 this.panelsHeight = 0; // Height of all the panels in the column
3691 this.height = 0; // Height of all the elements in the column
3692
3693 // Set panel resize partners
3694 panels.each(function(panel){
3695 currentInstance = instances.get(panel.id);
3696 if (panel.hasClass('expanded') && panel.getNext('.expanded')){
3697 currentInstance.partner = panel.getNext('.expanded');
3698 currentInstance.resize.attach();
3699 currentInstance.handleEl.setStyles({
3700 'display': 'block',
3701 'cursor': 'n-resize'
3702 }).removeClass('detached');
3703 }
3704 else {
3705 currentInstance.resize.detach();
3706 currentInstance.handleEl.setStyle('cursor', null).addClass('detached');
3707 }
3708 if (panel.getNext('.panel') == null){
3709 currentInstance.handleEl.setStyle('display', 'none');
3710 }
3711 }.bind(this));
3712
3713 // Get the total height of all the column's children
3714 column.getChildren().each(function(el){
3715
3716 if (el.hasClass('panel')){
3717 var currentInstance = instances.get(el.id);
3718
3719 // Are any next siblings Expanded?
3720 areAnyNextSiblingsExpanded = function(el){
3721 var test;
3722 el.getAllNext('.panel').each(function(sibling){
3723 var siblingInstance = instances.get(sibling.id);
3724 if (siblingInstance.isCollapsed == false){
3725 test = true;
3726 }
3727 }.bind(this));
3728 return test;
3729 }.bind(this);
3730
3731 // If a next sibling is expanding, are any of the nexts siblings of the expanding sibling Expanded?
3732 areAnyExpandingNextSiblingsExpanded = function(){
3733 var test;
3734 changing.getAllNext('.panel').each(function(sibling){
3735 var siblingInstance = instances.get(sibling.id);
3736 if (siblingInstance.isCollapsed == false){
3737 test = true;
3738 }
3739 }.bind(this));
3740 return test;
3741 }.bind(this);
3742
3743 // Resize panels that are not collapsed or "new"
3744 if (action == 'new' ) {
3745 if (currentInstance.isCollapsed != true && el != changing) {
3746 panelsToResize.push(el);
3747 }
3748
3749 // Height of panels that can be resized
3750 if (currentInstance.isCollapsed != true && el != changing) {
3751 this.panelsHeight += el.offsetHeight.toInt();
3752 }
3753 }
3754 // Resize panels that are not collapsed. If a panel is collapsing
3755 // resize any expanded panels below. If there are no expanded panels
3756 // below it, resize the expanded panels above it.
3757 else if (action == null || action == 'collapsing' ){
3758 if (currentInstance.isCollapsed != true && (el.getAllNext('.panel').contains(changing) != true || areAnyNextSiblingsExpanded(el) != true)){
3759 panelsToResize.push(el);
3760 }
3761
3762 // Height of panels that can be resized
3763 if (currentInstance.isCollapsed != true && (el.getAllNext('.panel').contains(changing) != true || areAnyNextSiblingsExpanded(el) != true)){
3764 this.panelsHeight += el.offsetHeight.toInt();
3765 }
3766 }
3767 // Resize panels that are not collapsed and are not expanding.
3768 // Resize any expanded panels below the expanding panel. If there are no expanded panels
3769 // below it, resize the first expanded panel above it.
3770 else if (action == 'expanding'){
3771
3772 if (currentInstance.isCollapsed != true && (el.getAllNext('.panel').contains(changing) != true || (areAnyExpandingNextSiblingsExpanded() != true && el.getNext('.expanded') == changing)) && el != changing){
3773 panelsToResize.push(el);
3774 }
3775 // Height of panels that can be resized
3776 if (currentInstance.isCollapsed != true && (el.getAllNext('.panel').contains(changing) != true || (areAnyExpandingNextSiblingsExpanded() != true && el.getNext('.expanded') == changing)) && el != changing){
3777 this.panelsHeight += el.offsetHeight.toInt();
3778 }
3779 }
3780
3781 if (el.style.height){
3782 this.height += el.getStyle('height').toInt();
3783 }
3784 }
3785 else {
3786 this.height += el.offsetHeight.toInt();
3787 }
3788 }.bind(this));
3789
3790 // Get the remaining height
3791 var remainingHeight = column.offsetHeight.toInt() - this.height;
3792
3793 this.height = 0;
3794
3795 // Get height of all the column's children
3796 column.getChildren().each(function(el){
3797 this.height += el.offsetHeight.toInt();
3798 }.bind(this));
3799
3800 var remainingHeight = column.offsetHeight.toInt() - this.height;
3801
3802 panelsToResize.each(function(panel){
3803 var ratio = this.panelsHeight / panel.offsetHeight.toInt();
3804 var newPanelHeight = panel.getStyle('height').toInt() + (remainingHeight / ratio);
3805 if (newPanelHeight < 1){
3806 newPanelHeight = 0;
3807 }
3808 panel.setStyle('height', newPanelHeight);
3809 }.bind(this));
3810
3811 // Make sure the remaining height is 0. If not add/subtract the
3812 // remaining height to the tallest panel. This makes up for browser resizing,
3813 // off ratios, and users trying to give panels too much height.
3814
3815 // Get height of all the column's children
3816 this.height = 0;
3817 column.getChildren().each(function(el){
3818 this.height += el.offsetHeight.toInt();
3819 if (el.hasClass('panel') && el.getStyle('height').toInt() > tallestPanelHeight){
3820 tallestPanel = el;
3821 tallestPanelHeight = el.getStyle('height').toInt();
3822 }
3823 }.bind(this));
3824
3825 var remainingHeight = column.offsetHeight.toInt() - this.height;
3826
3827 if ((remainingHeight > 0 || remainingHeight < 0) && tallestPanelHeight > 0){
3828 tallestPanel.setStyle('height', tallestPanel.getStyle('height').toInt() + remainingHeight );
3829 if (tallestPanel.getStyle('height') < 1){
3830 tallestPanel.setStyle('height', 0 );
3831 }
3832 }
3833
3834 $$('.columnHandle').each(function(handle){
3835 var handleHeight = parent.getStyle('height').toInt() - handle.getStyle('border-top').toInt() - handle.getStyle('border-bottom').toInt();
3836 if (Browser.Engine.trident4){
3837 handleHeight -= 1;
3838 }
3839 handle.setStyle('height', handleHeight);
3840 });
3841
3842 panelsExpanded.each(function(panel){
3843 MochaUI.resizeChildren(panel);
3844 }.bind(this));
3845 },
3846 // May rename this resizeIframeEl()
3847 resizeChildren: function(panel){
3848 var instances = MochaUI.Panels.instances;
3849 var currentInstance = instances.get(panel.id);
3850 var contentWrapperEl = currentInstance.contentWrapperEl;
3851
3852 if (currentInstance.iframeEl){
3853 currentInstance.iframeEl.setStyles({
3854 'height': contentWrapperEl.getStyle('height'),
3855 'width': contentWrapperEl.offsetWidth - contentWrapperEl.getStyle('border-left').toInt() - contentWrapperEl.getStyle('border-right').toInt()
3856 });
3857 }
3858 },
3859 // Remaining Width
3860 rWidth: function(){
3861 $$('.rWidth').each(function(column){
3862 var currentWidth = column.offsetWidth.toInt();
3863 currentWidth -= column.getStyle('border-left').toInt();
3864 currentWidth -= column.getStyle('border-right').toInt();
3865
3866 var parent = column.getParent();
3867 this.width = 0;
3868
3869 // Get the total width of all the parent element's children
3870 parent.getChildren().each(function(el){
3871 if (el.hasClass('mocha') != true){
3872 this.width += el.offsetWidth.toInt();
3873 }
3874 }.bind(this));
3875
3876 // Add the remaining width to the current element
3877 var remainingWidth = parent.offsetWidth.toInt() - this.width;
3878 var newWidth = currentWidth + remainingWidth;
3879 if (newWidth < 1) newWidth = 0;
3880 column.setStyle('width', newWidth);
3881 column.getChildren('.panel').each(function(panel){
3882 panel.setStyle('width', newWidth - panel.getStyle('border-left').toInt() - panel.getStyle('border-right').toInt());
3883 MochaUI.resizeChildren(panel);
3884 }.bind(this));
3885 });
3886 }
3887
3888 });
3889
3890 function addResizeRight(element, min, max){
3891 if (!$(element)) return;
3892 element = $(element);
3893
3894 var instances = MochaUI.Columns.instances;
3895 var currentInstance = instances.get(element.id);
3896
3897 var handle = element.getNext('.columnHandle');
3898 handle.setStyle('cursor', 'e-resize');
3899 if (!min) min = 50;
3900 if (!max) max = 250;
3901 if (Browser.Engine.trident){
3902 handle.addEvents({
3903 'mousedown': function(){
3904 handle.setCapture();
3905 },
3906 'mouseup': function(){
3907 handle.releaseCapture();
3908 }
3909 });
3910 }
3911 currentInstance.resize = element.makeResizable({
3912 handle: handle,
3913 modifiers: {x: 'width', y: false},
3914 limit: { x: [min, max] },
3915 onStart: function(){
3916 element.getElements('iframe').setStyle('visibility','hidden');
3917 element.getNext('.column').getElements('iframe').setStyle('visibility','hidden');
3918 }.bind(this),
3919 onDrag: function(){
3920 MochaUI.rWidth();
3921 if (Browser.Engine.trident4){
3922 element.getChildren().each(function(el){
3923 var width = $(element).getStyle('width').toInt();
3924 width -= el.getStyle('border-right').toInt();
3925 width -= el.getStyle('border-left').toInt();
3926 width -= el.getStyle('padding-right').toInt();
3927 width -= el.getStyle('padding-left').toInt();
3928 el.setStyle('width', width);
3929 }.bind(this));
3930 }
3931 }.bind(this),
3932 onComplete: function(){
3933 MochaUI.rWidth();
3934 element.getElements('iframe').setStyle('visibility','visible');
3935 element.getNext('.column').getElements('iframe').setStyle('visibility','visible');
3936 currentInstance.fireEvent('onResize');
3937 }.bind(this)
3938 });
3939 }
3940
3941 function addResizeLeft(element, min, max){
3942 if (!$(element)) return;
3943 element = $(element);
3944
3945 var instances = MochaUI.Columns.instances;
3946 var currentInstance = instances.get(element.id);
3947
3948 var handle = element.getPrevious('.columnHandle');
3949 handle.setStyle('cursor', 'e-resize');
3950 var partner = element.getPrevious('.column');
3951 if (!min) min = 50;
3952 if (!max) max = 250;
3953 if (Browser.Engine.trident){
3954 handle.addEvents({
3955 'mousedown': function(){
3956 handle.setCapture();
3957 },
3958 'mouseup': function(){
3959 handle.releaseCapture();
3960 }
3961 });
3962 }
3963 currentInstance.resize = element.makeResizable({
3964 handle: handle,
3965 modifiers: {x: 'width' , y: false},
3966 invert: true,
3967 limit: { x: [min, max] },
3968 onStart: function(){
3969 $(element).getElements('iframe').setStyle('visibility','hidden');
3970 partner.getElements('iframe').setStyle('visibility','hidden');
3971 }.bind(this),
3972 onDrag: function(){
3973 MochaUI.rWidth();
3974 }.bind(this),
3975 onComplete: function(){
3976 MochaUI.rWidth();
3977 $(element).getElements('iframe').setStyle('visibility','visible');
3978 partner.getElements('iframe').setStyle('visibility','visible');
3979 currentInstance.fireEvent('onResize');
3980 }.bind(this)
3981 });
3982 }
3983
3984 function addResizeBottom(element){
3985 if (!$(element)) return;
3986 var element = $(element);
3987
3988 var instances = MochaUI.Panels.instances;
3989 var currentInstance = instances.get(element.id);
3990 var handle = currentInstance.handleEl;
3991 handle.setStyle('cursor', 'n-resize');
3992 partner = currentInstance.partner;
3993 min = 0;
3994 max = function(){
3995 return element.getStyle('height').toInt() + partner.getStyle('height').toInt();
3996 }.bind(this);
3997
3998 if (Browser.Engine.trident){
3999 handle.addEvents({
4000 'mousedown': function(){
4001 handle.setCapture();
4002 },
4003 'mouseup': function(){
4004 handle.releaseCapture();
4005 }
4006 });
4007 }
4008 currentInstance.resize = element.makeResizable({
4009 handle: handle,
4010 modifiers: {x: false, y: 'height'},
4011 limit: { y: [min, max] },
4012 invert: false,
4013 onBeforeStart: function(){
4014 partner = currentInstance.partner;
4015 this.originalHeight = element.getStyle('height').toInt();
4016 this.partnerOriginalHeight = partner.getStyle('height').toInt();
4017 }.bind(this),
4018 onStart: function(){
4019 if (currentInstance.iframeEl) {
4020 currentInstance.iframeEl.setStyle('visibility', 'hidden');
4021 }
4022 partner.getElements('iframe').setStyle('visibility','hidden');
4023 }.bind(this),
4024 onDrag: function(){
4025 partnerHeight = partnerOriginalHeight + (this.originalHeight - element.getStyle('height').toInt());
4026 partner.setStyle('height', partnerHeight);
4027 MochaUI.resizeChildren(element, element.getStyle('height').toInt());
4028 MochaUI.resizeChildren(partner, partnerHeight);
4029 }.bind(this),
4030 onComplete: function(){
4031 partnerHeight = partnerOriginalHeight + (this.originalHeight - element.getStyle('height').toInt());
4032 partner.setStyle('height', partnerHeight);
4033 MochaUI.resizeChildren(element, element.getStyle('height').toInt());
4034 MochaUI.resizeChildren(partner, partnerHeight);
4035 if (currentInstance.iframeEl) {
4036 currentInstance.iframeEl.setStyle('visibility', 'visible');
4037 }
4038 partner.getElements('iframe').setStyle('visibility','visible');
4039 currentInstance.fireEvent('onResize');
4040 }.bind(this)
4041 });
4042 }
4043 /*
4044
4045 Script: Dock.js
4046 Implements the dock/taskbar. Enables window minimize.
4047
4048 Copyright:
4049 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
4050
4051 License:
4052 MIT-style license.
4053
4054 Requires:
4055 Core.js, Window.js, Layout.js
4056
4057 Todo:
4058 - Make it so the dock requires no initial html markup.
4059
4060 */
4061
4062 MochaUI.options.extend({
4063 // Naming options:
4064 // If you change the IDs of the Mocha Desktop containers in your HTML, you need to change them here as well.
4065 dockWrapper: 'dockWrapper',
4066 dock: 'dock'
4067 });
4068
4069 // Used by Desktop.js before MochaUI.Dock is initialized.
4070 window.addEvent('domready', function(){
4071 if ($('dockWrapper')) {
4072 MochaUI.dockVisible = true;
4073 }
4074 });
4075
4076 MochaUI.extend({
4077 /*
4078
4079 Function: minimizeAll
4080 Minimize all windows that are minimizable.
4081
4082 */
4083 minimizeAll: function() {
4084 $$('div.mocha').each(function(windowEl){
4085 var currentInstance = MochaUI.Windows.instances.get(windowEl.id);
4086 if (!currentInstance.isMinimized && currentInstance.options.minimizable == true){
4087 MochaUI.Dock.minimizeWindow(windowEl);
4088 }
4089 }.bind(this));
4090 }
4091 });
4092
4093 MochaUI.Dock = new Class({
4094 Extends: MochaUI.Window,
4095
4096 Implements: [Events, Options],
4097
4098 options: {
4099 useControls: true, // Toggles autohide and dock placement controls.
4100 dockPosition: 'top', // Position the dock starts in, top or bottom.
4101 // Style options
4102 dockTabColor: [255, 255, 255],
4103 trueButtonColor: [70, 245, 70], // Color for autohide on
4104 enabledButtonColor: [125, 208, 250],
4105 disabledButtonColor: [170, 170, 170]
4106 },
4107 initialize: function(options){
4108 // Stops if MochaUI.Desktop is not implemented
4109 if (!MochaUI.Desktop) return;
4110 this.setOptions(options);
4111
4112 this.dockWrapper = $(MochaUI.options.dockWrapper);
4113 this.dock = $(MochaUI.options.dock);
4114 this.autoHideEvent = null;
4115 this.dockAutoHide = false; // True when dock autohide is set to on, false if set to off
4116
4117 if (!this.dockWrapper) return;
4118
4119 if (!this.options.useControls){
4120 if($('dockPlacement')){
4121 $('dockPlacement').setStyle('cursor', 'default');
4122 }
4123 if($('dockAutoHide')){
4124 $('dockAutoHide').setStyle('cursor', 'default');
4125 }
4126 }
4127
4128 this.dockWrapper.setStyles({
4129 'display': 'block',
4130 'position': 'absolute',
4131 'top': null,
4132 'bottom': MochaUI.Desktop.desktopFooter ? MochaUI.Desktop.desktopFooter.offsetHeight : 0,
4133 'left': 0
4134 });
4135
4136 if (this.options.useControls){
4137 this.initializeDockControls();
4138 }
4139
4140 // Add check mark to menu if link exists in menu
4141 if ($('dockLinkCheck')){
4142 this.sidebarCheck = new Element('div', {
4143 'class': 'check',
4144 'id': 'dock_check'
4145 }).inject($('dockLinkCheck'));
4146 }
4147
4148 this.dockSortables = new Sortables('#dockSort', {
4149 opacity: Browser.Engine.trident ? 1 : .5,
4150 constrain: true,
4151 clone: false,
4152 revert: false
4153 });
4154
4155 MochaUI.Desktop.setDesktopSize();
4156 },
4157 initializeDockControls: function(){
4158
4159 if (this.options.useControls){
4160 // Insert canvas
4161 var canvas = new Element('canvas', {
4162 'id': 'dockCanvas',
4163 'width': '15',
4164 'height': '18'
4165 }).inject(this.dock);
4166
4167 // Dynamically initialize canvas using excanvas. This is only required by IE
4168 if (Browser.Engine.trident && MochaUI.ieSupport == 'excanvas'){
4169 G_vmlCanvasManager.initElement(canvas);
4170 }
4171 }
4172
4173 var dockPlacement = $('dockPlacement');
4174 var dockAutoHide = $('dockAutoHide');
4175
4176 // Position top or bottom selector
4177 dockPlacement.setProperty('title','Position Dock Top');
4178
4179 // Attach event
4180 dockPlacement.addEvent('click', function(){
4181 this.moveDock();
4182 }.bind(this));
4183
4184 // Auto Hide toggle switch
4185 dockAutoHide.setProperty('title','Turn Auto Hide On');
4186
4187 // Attach event Auto Hide
4188 dockAutoHide.addEvent('click', function(event){
4189 if ( this.dockWrapper.getProperty('dockPosition') == 'top' )
4190 return false;
4191
4192 var ctx = $('dockCanvas').getContext('2d');
4193 this.dockAutoHide = !this.dockAutoHide; // Toggle
4194 if (this.dockAutoHide){
4195 $('dockAutoHide').setProperty('title', 'Turn Auto Hide Off');
4196 //ctx.clearRect(0, 11, 100, 100);
4197 MochaUI.circle(ctx, 5 , 14, 3, this.options.trueButtonColor, 1.0);
4198
4199 // Define event
4200 this.autoHideEvent = function(event) {
4201 if (!this.dockAutoHide)
4202 return;
4203 if (!MochaUI.Desktop.desktopFooter) {
4204 var dockHotspotHeight = this.dockWrapper.offsetHeight;
4205 if (dockHotspotHeight < 25) dockHotspotHeight = 25;
4206 }
4207 else if (MochaUI.Desktop.desktopFooter) {
4208 var dockHotspotHeight = this.dockWrapper.offsetHeight + MochaUI.Desktop.desktopFooter.offsetHeight;
4209 if (dockHotspotHeight < 25) dockHotspotHeight = 25;
4210 }
4211 if (!MochaUI.Desktop.desktopFooter && event.client.y > (document.getCoordinates().height - dockHotspotHeight)){
4212 if (!MochaUI.dockVisible){
4213 this.dockWrapper.setStyle('display', 'block');
4214 MochaUI.dockVisible = true;
4215 MochaUI.Desktop.setDesktopSize();
4216 }
4217 }
4218 else if (MochaUI.Desktop.desktopFooter && event.client.y > (document.getCoordinates().height - dockHotspotHeight)){
4219 if (!MochaUI.dockVisible){
4220 this.dockWrapper.setStyle('display', 'block');
4221 MochaUI.dockVisible = true;
4222 MochaUI.Desktop.setDesktopSize();
4223 }
4224 }
4225 else if (MochaUI.dockVisible){
4226 this.dockWrapper.setStyle('display', 'none');
4227 MochaUI.dockVisible = false;
4228 MochaUI.Desktop.setDesktopSize();
4229
4230 }
4231 }.bind(this);
4232
4233 // Add event
4234 document.addEvent('mousemove', this.autoHideEvent);
4235
4236 } else {
4237 $('dockAutoHide').setProperty('title', 'Turn Auto Hide On');
4238 //ctx.clearRect(0, 11, 100, 100);
4239 MochaUI.circle(ctx, 5 , 14, 3, this.options.enabledButtonColor, 1.0);
4240 // Remove event
4241 document.removeEvent('mousemove', this.autoHideEvent);
4242 }
4243
4244 }.bind(this));
4245
4246 // Draw dock controls
4247 var ctx = $('dockCanvas').getContext('2d');
4248 ctx.clearRect(0, 0, 100, 100);
4249 MochaUI.circle(ctx, 5 , 4, 3, this.options.enabledButtonColor, 1.0);
4250 MochaUI.circle(ctx, 5 , 14, 3, this.options.enabledButtonColor, 1.0);
4251
4252 if (this.options.dockPosition == 'top'){
4253 this.moveDock();
4254 }
4255
4256 },
4257 moveDock: function(){
4258 var ctx = $('dockCanvas').getContext('2d');
4259 // Move dock to top position
4260 if (this.dockWrapper.getStyle('position') != 'relative'){
4261 this.dockWrapper.setStyles({
4262 'position': 'relative',
4263 'bottom': null
4264 });
4265 this.dockWrapper.addClass('top');
4266 MochaUI.Desktop.setDesktopSize();
4267 this.dockWrapper.setProperty('dockPosition','top');
4268 ctx.clearRect(0, 0, 100, 100);
4269 MochaUI.circle(ctx, 5, 4, 3, this.options.enabledButtonColor, 1.0);
4270 MochaUI.circle(ctx, 5, 14, 3, this.options.disabledButtonColor, 1.0);
4271 $('dockPlacement').setProperty('title', 'Position Dock Bottom');
4272 $('dockAutoHide').setProperty('title', 'Auto Hide Disabled in Top Dock Position');
4273 this.dockAutoHide = false;
4274 }
4275 // Move dock to bottom position
4276 else {
4277 this.dockWrapper.setStyles({
4278 'position': 'absolute',
4279 'bottom': MochaUI.Desktop.desktopFooter ? MochaUI.Desktop.desktopFooter.offsetHeight : 0
4280 });
4281 this.dockWrapper.removeClass('top');
4282 MochaUI.Desktop.setDesktopSize();
4283 this.dockWrapper.setProperty('dockPosition', 'bottom');
4284 ctx.clearRect(0, 0, 100, 100);
4285 MochaUI.circle(ctx, 5, 4, 3, this.options.enabledButtonColor, 1.0);
4286 MochaUI.circle(ctx, 5 , 14, 3, this.options.enabledButtonColor, 1.0);
4287 $('dockPlacement').setProperty('title', 'Position Dock Top');
4288 $('dockAutoHide').setProperty('title', 'Turn Auto Hide On');
4289 }
4290 },
4291 createDockTab: function(windowEl){
4292
4293 var currentInstance = MochaUI.Windows.instances.get(windowEl.id);
4294
4295 var dockTab = new Element('div', {
4296 'id': currentInstance.options.id + '_dockTab',
4297 'class': 'dockTab',
4298 'title': titleText
4299 }).inject($('dockClear'), 'before');
4300
4301 dockTab.addEvent('mousedown', function(e){
4302 new Event(e).stop();
4303 this.timeDown = $time();
4304 });
4305
4306 dockTab.addEvent('mouseup', function(e){
4307 this.timeUp = $time();
4308 if ((this.timeUp - this.timeDown) < 275){
4309 // If the visibility of the windows on the page are toggled off, toggle visibility on.
4310 if (MochaUI.Windows.windowsVisible == false) {
4311 MochaUI.toggleWindowVisibility();
4312 if (currentInstance.isMinimized == true) {
4313 MochaUI.Dock.restoreMinimized.delay(25, MochaUI.Dock, windowEl);
4314 }
4315 else {
4316 MochaUI.focusWindow(windowEl);
4317 }
4318 return;
4319 }
4320 // If window is minimized, restore window.
4321 if (currentInstance.isMinimized == true) {
4322 MochaUI.Dock.restoreMinimized.delay(25, MochaUI.Dock, windowEl);
4323 }
4324 else{
4325 // If window is not minimized and is focused, minimize window.
4326 if (currentInstance.windowEl.hasClass('isFocused') && currentInstance.options.minimizable == true){
4327 MochaUI.Dock.minimizeWindow(windowEl)
4328 }
4329 // If window is not minimized and is not focused, focus window.
4330 else{
4331 MochaUI.focusWindow(windowEl);
4332 }
4333 // if the window is not minimized and is outside the viewport, center it in the viewport.
4334 var coordinates = document.getCoordinates();
4335 if (windowEl.getStyle('left').toInt() > coordinates.width || windowEl.getStyle('top').toInt() > coordinates.height){
4336 MochaUI.centerWindow(windowEl);
4337 }
4338 }
4339 }
4340 });
4341
4342 this.dockSortables.addItems(dockTab);
4343
4344 var titleText = currentInstance.titleEl.innerHTML;
4345
4346 var dockTabText = new Element('div', {
4347 'id': currentInstance.options.id + '_dockTabText',
4348 'class': 'dockText'
4349 }).set('html', titleText.substring(0,20) + (titleText.length > 20 ? '...' : '')).inject($(dockTab));
4350
4351 // If I implement this again, will need to also adjust the titleText truncate and the tab's
4352 // left padding.
4353 if (currentInstance.options.icon != false){
4354 // dockTabText.setStyle('background', 'url(' + currentInstance.options.icon + ') 4px 4px no-repeat');
4355 }
4356
4357 // Need to resize everything in case the dock wraps when a new tab is added
4358 MochaUI.Desktop.setDesktopSize();
4359
4360 },
4361 makeActiveTab: function(){
4362
4363 // getWindowWith HighestZindex is used in case the currently focused window
4364 // is closed.
4365 var windowEl = MochaUI.getWindowWithHighestZindex();
4366 var currentInstance = MochaUI.Windows.instances.get(windowEl.id);
4367
4368 $$('div.dockTab').removeClass('activeDockTab');
4369 if (currentInstance.isMinimized != true) {
4370
4371 currentInstance.windowEl.addClass('isFocused');
4372
4373 var currentButton = $(currentInstance.options.id + '_dockTab');
4374 if (currentButton != null) {
4375 currentButton.addClass('activeDockTab');
4376 }
4377 }
4378 else {
4379 currentInstance.windowEl.removeClass('isFocused');
4380 }
4381 },
4382 minimizeWindow: function(windowEl){
4383 if (windowEl != $(windowEl)) return;
4384
4385 var currentInstance = MochaUI.Windows.instances.get(windowEl.id);
4386 currentInstance.isMinimized = true;
4387
4388 // Hide iframe
4389 // Iframe should be hidden when minimizing, maximizing, and moving for performance and Flash issues
4390 if ( currentInstance.iframeEl ) {
4391 currentInstance.iframeEl.setStyle('visibility', 'hidden');
4392 }
4393
4394 // Hide window and add to dock
4395 currentInstance.contentBorderEl.setStyle('visibility', 'hidden');
4396 if(currentInstance.toolbarWrapperEl){
4397 currentInstance.toolbarWrapperEl.setStyle('visibility', 'hidden');
4398 }
4399 windowEl.setStyle('visibility', 'hidden');
4400
4401 // Fixes a scrollbar issue in Mac FF2
4402 if (Browser.Platform.mac && Browser.Engine.gecko){
4403 if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)) {
4404 var ffversion = new Number(RegExp.$1);
4405 if (ffversion < 3) {
4406 currentInstance.contentWrapperEl.setStyle('overflow', 'hidden');
4407 }
4408 }
4409 }
4410
4411 MochaUI.Desktop.setDesktopSize();
4412
4413 // Have to use timeout because window gets focused when you click on the minimize button
4414 setTimeout(function(){
4415 windowEl.setStyle('zIndex', 1);
4416 windowEl.removeClass('isFocused');
4417 this.makeActiveTab();
4418 }.bind(this),100);
4419
4420 currentInstance.fireEvent('onMinimize', windowEl);
4421 },
4422 restoreMinimized: function(windowEl) {
4423
4424 var currentInstance = MochaUI.Windows.instances.get(windowEl.id);
4425
4426 if (currentInstance.isMinimized == false) return;
4427
4428 if (MochaUI.Windows.windowsVisible == false){
4429 MochaUI.toggleWindowVisibility();
4430 }
4431
4432 MochaUI.Desktop.setDesktopSize();
4433
4434 // Part of Mac FF2 scrollbar fix
4435 if (currentInstance.options.scrollbars == true && !currentInstance.iframeEl){
4436 currentInstance.contentWrapperEl.setStyle('overflow', 'auto');
4437 }
4438
4439 if (currentInstance.isCollapsed) {
4440 MochaUI.collapseToggle(windowEl);
4441 }
4442
4443 windowEl.setStyle('visibility', 'visible');
4444 currentInstance.contentBorderEl.setStyle('visibility', 'visible');
4445 if(currentInstance.toolbarWrapperEl){
4446 currentInstance.toolbarWrapperEl.setStyle('visibility', 'visible');
4447 }
4448
4449 // Show iframe
4450 if ( currentInstance.iframeEl ) {
4451 currentInstance.iframeEl.setStyle('visibility', 'visible');
4452 }
4453
4454 currentInstance.isMinimized = false;
4455 MochaUI.focusWindow(windowEl);
4456 currentInstance.fireEvent('onRestore', windowEl);
4457
4458 }
4459 });
4460 MochaUI.Dock.implement(new Options, new Events);
4461 /*
4462
4463 Script: Workspaces.js
4464 Save and load workspaces. The Workspaces emulate Adobe Illustrator functionality remembering what windows are open and where they are positioned. There will be two versions, a limited version that saves state to a cookie, and a fully functional version that saves state to a database.
4465
4466 Copyright:
4467 Copyright (c) 2007-2008 Greg Houston, <http://greghoustondesign.com/>.
4468
4469 License:
4470 MIT-style license.
4471
4472 Requires:
4473 Core.js, Window.js
4474
4475 To do:
4476 - Move to Window
4477
4478 */
4479
4480 MochaUI.extend({
4481 /*
4482
4483 Function: saveWorkspace
4484 Save the current workspace.
4485
4486 Syntax:
4487 (start code)
4488 MochaUI.saveWorkspace();
4489 (end)
4490
4491 Notes:
4492 This is experimental. 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.
4493
4494 */
4495 saveWorkspace: function(){
4496 this.cookie = new Hash.Cookie('mochaUIworkspaceCookie', {duration: 3600});
4497 this.cookie.empty();
4498 MochaUI.Windows.instances.each(function(instance) {
4499 instance.saveValues();
4500 this.cookie.set(instance.options.id, {
4501 'id': instance.options.id,
4502 'top': instance.options.y,
4503 'left': instance.options.x
4504 });
4505 }.bind(this));
4506 this.cookie.save();
4507
4508 new MochaUI.Window({
4509 loadMethod: 'html',
4510 type: 'notification',
4511 addClass: 'notification',
4512 content: 'Workspace saved.',
4513 closeAfter: '1400',
4514 width: 200,
4515 height: 40,
4516 y: 53,
4517 padding: { top: 10, right: 12, bottom: 10, left: 12 },
4518 shadowBlur: 5,
4519 bodyBgColor: [255, 255, 255]
4520 });
4521
4522 },
4523 windowUnload: function(){
4524 if ($$('div.mocha').length == 0 && this.myChain){
4525 this.myChain.callChain();
4526 }
4527 },
4528 loadWorkspace2: function(workspaceWindows){
4529 workspaceWindows.each(function(instance){
4530 windowFunction = eval('MochaUI.' + instance.id + 'Window');
4531 if (windowFunction){
4532 eval('MochaUI.' + instance.id + 'Window();');
4533 $(instance.id).setStyles({
4534 top: instance.top,
4535 left: instance.left
4536 });
4537 }
4538 }.bind(this));
4539 this.loadingWorkspace = false;
4540 },
4541 /*
4542
4543 Function: loadWorkspace
4544 Load the saved workspace.
4545
4546 Syntax:
4547 (start code)
4548 MochaUI.loadWorkspace();
4549 (end)
4550
4551 */
4552 loadWorkspace: function(){
4553 cookie = new Hash.Cookie('mochaUIworkspaceCookie', {duration: 3600});
4554 workspaceWindows = cookie.load();
4555
4556 if(!cookie.getKeys().length){
4557 new MochaUI.Window({
4558 loadMethod: 'html',
4559 type: 'notification',
4560 addClass: 'notification',
4561 content: 'You have no saved workspace.',
4562 closeAfter: '1400',
4563 width: 220,
4564 height: 40,
4565 y: 25,
4566 padding: { top: 10, right: 12, bottom: 10, left: 12 },
4567 shadowBlur: 5,
4568 bodyBgColor: [255, 255, 255]
4569 });
4570 return;
4571 }
4572
4573 if ($$('div.mocha').length != 0){
4574 this.loadingWorkspace = true;
4575 this.myChain = new Chain();
4576 this.myChain.chain(
4577 function(){
4578 $$('div.mocha').each(function(el) {
4579 this.closeWindow(el);
4580 }.bind(this));
4581 }.bind(this),
4582 function(){
4583 this.loadWorkspace2(workspaceWindows);
4584 }.bind(this)
4585 );
4586 this.myChain.callChain();
4587 }
4588 else {
4589 this.loadWorkspace2(workspaceWindows);
4590 }
4591
4592 }
4593 });