web/wp-admin/js/wp-fullscreen.dev.js
changeset 194 32102edaa81b
equal deleted inserted replaced
193:2f6f6f7551ca 194:32102edaa81b
       
     1 /**
       
     2  * PubSub
       
     3  *
       
     4  * A lightweight publish/subscribe implementation.
       
     5  * Private use only!
       
     6  */
       
     7 var PubSub, fullscreen, wptitlehint;
       
     8 
       
     9 PubSub = function() {
       
    10 	this.topics = {};
       
    11 };
       
    12 
       
    13 PubSub.prototype.subscribe = function( topic, callback ) {
       
    14 	if ( ! this.topics[ topic ] )
       
    15 		this.topics[ topic ] = [];
       
    16 
       
    17 	this.topics[ topic ].push( callback );
       
    18 	return callback;
       
    19 };
       
    20 
       
    21 PubSub.prototype.unsubscribe = function( topic, callback ) {
       
    22 	var i, l,
       
    23 		topics = this.topics[ topic ];
       
    24 
       
    25 	if ( ! topics )
       
    26 		return callback || [];
       
    27 
       
    28 	// Clear matching callbacks
       
    29 	if ( callback ) {
       
    30 		for ( i = 0, l = topics.length; i < l; i++ ) {
       
    31 			if ( callback == topics[i] )
       
    32 				topics.splice( i, 1 );
       
    33 		}
       
    34 		return callback;
       
    35 
       
    36 	// Clear all callbacks
       
    37 	} else {
       
    38 		this.topics[ topic ] = [];
       
    39 		return topics;
       
    40 	}
       
    41 };
       
    42 
       
    43 PubSub.prototype.publish = function( topic, args ) {
       
    44 	var i, l, broken,
       
    45 		topics = this.topics[ topic ];
       
    46 
       
    47 	if ( ! topics )
       
    48 		return;
       
    49 
       
    50 	args = args || [];
       
    51 
       
    52 	for ( i = 0, l = topics.length; i < l; i++ ) {
       
    53 		broken = ( topics[i].apply( null, args ) === false || broken );
       
    54 	}
       
    55 	return ! broken;
       
    56 };
       
    57 
       
    58 /**
       
    59  * Distraction Free Writing
       
    60  * (wp-fullscreen)
       
    61  *
       
    62  * Access the API globally using the fullscreen variable.
       
    63  */
       
    64 
       
    65 (function($){
       
    66 	var api, ps, bounder, s;
       
    67 
       
    68 	// Initialize the fullscreen/api object
       
    69 	fullscreen = api = {};
       
    70 
       
    71 	// Create the PubSub (publish/subscribe) interface.
       
    72 	ps = api.pubsub = new PubSub();
       
    73 	timer = 0;
       
    74 	block = false;
       
    75 
       
    76 	s = api.settings = { // Settings
       
    77 		visible : false,
       
    78 		mode : 'tinymce',
       
    79 		editor_id : 'content',
       
    80 		title_id : '',
       
    81 		timer : 0,
       
    82 		toolbar_shown : false
       
    83 	}
       
    84 
       
    85 	/**
       
    86 	 * Bounder
       
    87 	 *
       
    88 	 * Creates a function that publishes start/stop topics.
       
    89 	 * Used to throttle events.
       
    90 	 */
       
    91 	bounder = api.bounder = function( start, stop, delay, e ) {
       
    92 		var y, top;
       
    93 
       
    94 		delay = delay || 1250;
       
    95 
       
    96 		if ( e ) {
       
    97 			y = e.pageY || e.clientY || e.offsetY;
       
    98 			top = $(document).scrollTop();
       
    99 
       
   100 			if ( !e.isDefaultPrevented ) // test if e ic jQuery normalized
       
   101 				y = 135 + y;
       
   102 
       
   103 			if ( y - top > 120 )
       
   104 				return;
       
   105 		}
       
   106 
       
   107 		if ( block )
       
   108 			return;
       
   109 
       
   110 		block = true;
       
   111 
       
   112 		setTimeout( function() {
       
   113 			block = false;
       
   114 		}, 400 );
       
   115 
       
   116 		if ( s.timer )
       
   117 			clearTimeout( s.timer );
       
   118 		else
       
   119 			ps.publish( start );
       
   120 
       
   121 		function timed() {
       
   122 			ps.publish( stop );
       
   123 			s.timer = 0;
       
   124 		}
       
   125 
       
   126 		s.timer = setTimeout( timed, delay );
       
   127 	};
       
   128 
       
   129 	/**
       
   130 	 * on()
       
   131 	 *
       
   132 	 * Turns fullscreen on.
       
   133 	 *
       
   134 	 * @param string mode Optional. Switch to the given mode before opening.
       
   135 	 */
       
   136 	api.on = function() {
       
   137 		if ( s.visible )
       
   138 			return;
       
   139 
       
   140 		// Settings can be added or changed by defining "wp_fullscreen_settings" JS object.
       
   141 		if ( typeof(wp_fullscreen_settings) == 'object' )
       
   142 			$.extend( s, wp_fullscreen_settings );
       
   143 
       
   144 		s.editor_id = wpActiveEditor || 'content';
       
   145 
       
   146 		if ( $('input#title').length && s.editor_id == 'content' )
       
   147 			s.title_id = 'title';
       
   148 		else if ( $('input#' + s.editor_id + '-title').length ) // the title input field should have [editor_id]-title HTML ID to be auto detected
       
   149 			s.title_id = s.editor_id + '-title';
       
   150 		else
       
   151 			$('#wp-fullscreen-title, #wp-fullscreen-title-prompt-text').hide();
       
   152 
       
   153 		s.mode = $('#' + s.editor_id).is(':hidden') ? 'tinymce' : 'html';
       
   154 		s.qt_canvas = $('#' + s.editor_id).get(0);
       
   155 
       
   156 		if ( ! s.element )
       
   157 			api.ui.init();
       
   158 
       
   159 		s.is_mce_on = s.has_tinymce && typeof( tinyMCE.get(s.editor_id) ) != 'undefined';
       
   160 
       
   161 		api.ui.fade( 'show', 'showing', 'shown' );
       
   162 	};
       
   163 
       
   164 	/**
       
   165 	 * off()
       
   166 	 *
       
   167 	 * Turns fullscreen off.
       
   168 	 */
       
   169 	api.off = function() {
       
   170 		if ( ! s.visible )
       
   171 			return;
       
   172 
       
   173 		api.ui.fade( 'hide', 'hiding', 'hidden' );
       
   174 	};
       
   175 
       
   176 	/**
       
   177 	 * switchmode()
       
   178 	 *
       
   179 	 * @return string - The current mode.
       
   180 	 *
       
   181 	 * @param string to - The fullscreen mode to switch to.
       
   182 	 * @event switchMode
       
   183 	 * @eventparam string to   - The new mode.
       
   184 	 * @eventparam string from - The old mode.
       
   185 	 */
       
   186 	api.switchmode = function( to ) {
       
   187 		var from = s.mode;
       
   188 
       
   189 		if ( ! to || ! s.visible || ! s.has_tinymce )
       
   190 			return from;
       
   191 
       
   192 		// Don't switch if the mode is the same.
       
   193 		if ( from == to )
       
   194 			return from;
       
   195 
       
   196 		ps.publish( 'switchMode', [ from, to ] );
       
   197 		s.mode = to;
       
   198 		ps.publish( 'switchedMode', [ from, to ] );
       
   199 
       
   200 		return to;
       
   201 	};
       
   202 
       
   203 	/**
       
   204 	 * General
       
   205 	 */
       
   206 
       
   207 	api.save = function() {
       
   208 		var hidden = $('#hiddenaction'), old = hidden.val(), spinner = $('#wp-fullscreen-save img'),
       
   209 			message = $('#wp-fullscreen-save span');
       
   210 
       
   211 		spinner.show();
       
   212 		api.savecontent();
       
   213 
       
   214 		hidden.val('wp-fullscreen-save-post');
       
   215 
       
   216 		$.post( ajaxurl, $('form#post').serialize(), function(r){
       
   217 			spinner.hide();
       
   218 			message.show();
       
   219 
       
   220 			setTimeout( function(){
       
   221 				message.fadeOut(1000);
       
   222 			}, 3000 );
       
   223 
       
   224 			if ( r.last_edited )
       
   225 				$('#wp-fullscreen-save input').attr( 'title',  r.last_edited );
       
   226 
       
   227 		}, 'json');
       
   228 
       
   229 		hidden.val(old);
       
   230 	}
       
   231 
       
   232 	api.savecontent = function() {
       
   233 		var ed, content;
       
   234 
       
   235 		if ( s.title_id )
       
   236 			$('#' + s.title_id).val( $('#wp-fullscreen-title').val() );
       
   237 
       
   238 		if ( s.mode === 'tinymce' && (ed = tinyMCE.get('wp_mce_fullscreen')) ) {
       
   239 			content = ed.save();
       
   240 		} else {
       
   241 			content = $('#wp_mce_fullscreen').val();
       
   242 		}
       
   243 
       
   244 		$('#' + s.editor_id).val( content );
       
   245 		$(document).triggerHandler('wpcountwords', [ content ]);
       
   246 	}
       
   247 
       
   248 	set_title_hint = function( title ) {
       
   249 		if ( ! title.val().length )
       
   250 			title.siblings('label').css( 'visibility', '' );
       
   251 		else
       
   252 			title.siblings('label').css( 'visibility', 'hidden' );
       
   253 	}
       
   254 
       
   255 	api.dfw_width = function(n) {
       
   256 		var el = $('#wp-fullscreen-wrap'), w = el.width();
       
   257 
       
   258 		if ( !n ) { // reset to theme width
       
   259 			el.width( $('#wp-fullscreen-central-toolbar').width() );
       
   260 			deleteUserSetting('dfw_width');
       
   261 			return;
       
   262 		}
       
   263 
       
   264 		w = n + w;
       
   265 
       
   266 		if ( w < 200 || w > 1200 ) // sanity check
       
   267 			return;
       
   268 
       
   269 		el.width( w );
       
   270 		setUserSetting('dfw_width', w);
       
   271 	}
       
   272 
       
   273 	ps.subscribe( 'showToolbar', function() {
       
   274 		s.toolbars.removeClass('fade-1000').addClass('fade-300');
       
   275 		api.fade.In( s.toolbars, 300, function(){ ps.publish('toolbarShown'); }, true );
       
   276 		$('#wp-fullscreen-body').addClass('wp-fullscreen-focus');
       
   277 		s.toolbar_shown = true;
       
   278 	});
       
   279 
       
   280 	ps.subscribe( 'hideToolbar', function() {
       
   281 		s.toolbars.removeClass('fade-300').addClass('fade-1000');
       
   282 		api.fade.Out( s.toolbars, 1000, function(){ ps.publish('toolbarHidden'); }, true );
       
   283 		$('#wp-fullscreen-body').removeClass('wp-fullscreen-focus');
       
   284 	});
       
   285 
       
   286 	ps.subscribe( 'toolbarShown', function() {
       
   287 		s.toolbars.removeClass('fade-300');
       
   288 	});
       
   289 
       
   290 	ps.subscribe( 'toolbarHidden', function() {
       
   291 		s.toolbars.removeClass('fade-1000');
       
   292 		s.toolbar_shown = false;
       
   293 	});
       
   294 
       
   295 	ps.subscribe( 'show', function() { // This event occurs before the overlay blocks the UI.
       
   296 		var title;
       
   297 
       
   298 		if ( s.title_id ) {
       
   299 			title = $('#wp-fullscreen-title').val( $('#' + s.title_id).val() );
       
   300 			set_title_hint( title );
       
   301 		}
       
   302 
       
   303 		$('#wp-fullscreen-save input').attr( 'title',  $('#last-edit').text() );
       
   304 
       
   305 		s.textarea_obj.value = s.qt_canvas.value;
       
   306 
       
   307 		if ( s.has_tinymce && s.mode === 'tinymce' )
       
   308 			tinyMCE.execCommand('wpFullScreenInit');
       
   309 
       
   310 		s.orig_y = $(window).scrollTop();
       
   311 	});
       
   312 
       
   313 	ps.subscribe( 'showing', function() { // This event occurs while the DFW overlay blocks the UI.
       
   314 		$( document.body ).addClass( 'fullscreen-active' );
       
   315 		api.refresh_buttons();
       
   316 
       
   317 		$( document ).bind( 'mousemove.fullscreen', function(e) { bounder( 'showToolbar', 'hideToolbar', 2000, e ); } );
       
   318 		bounder( 'showToolbar', 'hideToolbar', 2000 );
       
   319 
       
   320 		api.bind_resize();
       
   321 		setTimeout( api.resize_textarea, 200 );
       
   322 
       
   323 		// scroll to top so the user is not disoriented
       
   324 		scrollTo(0, 0);
       
   325 
       
   326 		// needed it for IE7 and compat mode
       
   327 		$('#wpadminbar').hide();
       
   328 	});
       
   329 
       
   330 	ps.subscribe( 'shown', function() { // This event occurs after the DFW overlay is shown
       
   331 		var interim_init;
       
   332 
       
   333 		s.visible = true;
       
   334 
       
   335 		// init the standard TinyMCE instance if missing
       
   336 		if ( s.has_tinymce && ! s.is_mce_on ) {
       
   337 
       
   338 			interim_init = function(mce, ed) {
       
   339 				var el = ed.getElement(), old_val = el.value, settings = tinyMCEPreInit.mceInit[s.editor_id];
       
   340 
       
   341 				if ( settings && settings.wpautop && typeof(switchEditors) != 'undefined' )
       
   342 					el.value = switchEditors.wpautop( el.value );
       
   343 
       
   344 				ed.onInit.add(function(ed) {
       
   345 					ed.hide();
       
   346 					ed.getElement().value = old_val;
       
   347 					tinymce.onAddEditor.remove(interim_init);
       
   348 				});
       
   349 			};
       
   350 
       
   351 			tinymce.onAddEditor.add(interim_init);
       
   352 			tinyMCE.init(tinyMCEPreInit.mceInit[s.editor_id]);
       
   353 
       
   354 			s.is_mce_on = true;
       
   355 		}
       
   356 
       
   357 		wpActiveEditor = 'wp_mce_fullscreen';
       
   358 	});
       
   359 
       
   360 	ps.subscribe( 'hide', function() { // This event occurs before the overlay blocks DFW.
       
   361 		var htmled_is_hidden = $('#' + s.editor_id).is(':hidden');
       
   362 		// Make sure the correct editor is displaying.
       
   363 		if ( s.has_tinymce && s.mode === 'tinymce' && !htmled_is_hidden ) {
       
   364 			switchEditors.go(s.editor_id, 'tmce');
       
   365 		} else if ( s.mode === 'html' && htmled_is_hidden ) {
       
   366 			switchEditors.go(s.editor_id, 'html');
       
   367 		}
       
   368 
       
   369 		// Save content must be after switchEditors or content will be overwritten. See #17229.
       
   370 		api.savecontent();
       
   371 
       
   372 		$( document ).unbind( '.fullscreen' );
       
   373 		$(s.textarea_obj).unbind('.grow');
       
   374 
       
   375 		if ( s.has_tinymce && s.mode === 'tinymce' )
       
   376 			tinyMCE.execCommand('wpFullScreenSave');
       
   377 
       
   378 		if ( s.title_id )
       
   379 			set_title_hint( $('#' + s.title_id) );
       
   380 
       
   381 		s.qt_canvas.value = s.textarea_obj.value;
       
   382 	});
       
   383 
       
   384 	ps.subscribe( 'hiding', function() { // This event occurs while the overlay blocks the DFW UI.
       
   385 
       
   386 		$( document.body ).removeClass( 'fullscreen-active' );
       
   387 		scrollTo(0, s.orig_y);
       
   388 		$('#wpadminbar').show();
       
   389 	});
       
   390 
       
   391 	ps.subscribe( 'hidden', function() { // This event occurs after DFW is removed.
       
   392 		s.visible = false;
       
   393 		$('#wp_mce_fullscreen, #wp-fullscreen-title').removeAttr('style');
       
   394 
       
   395 		if ( s.has_tinymce && s.is_mce_on )
       
   396 			tinyMCE.execCommand('wpFullScreenClose');
       
   397 
       
   398 		s.textarea_obj.value = '';
       
   399 		api.oldheight = 0;
       
   400 		wpActiveEditor = s.editor_id;
       
   401 	});
       
   402 
       
   403 	ps.subscribe( 'switchMode', function( from, to ) {
       
   404 		var ed;
       
   405 
       
   406 		if ( !s.has_tinymce || !s.is_mce_on )
       
   407 			return;
       
   408 
       
   409 		ed = tinyMCE.get('wp_mce_fullscreen');
       
   410 
       
   411 		if ( from === 'html' && to === 'tinymce' ) {
       
   412 
       
   413 			if ( tinyMCE.get(s.editor_id).getParam('wpautop') && typeof(switchEditors) != 'undefined' )
       
   414 				s.textarea_obj.value = switchEditors.wpautop( s.textarea_obj.value );
       
   415 
       
   416 			if ( 'undefined' == typeof(ed) )
       
   417 				tinyMCE.execCommand('wpFullScreenInit');
       
   418 			else
       
   419 				ed.show();
       
   420 
       
   421 		} else if ( from === 'tinymce' && to === 'html' ) {
       
   422 			if ( ed )
       
   423 				ed.hide();
       
   424 		}
       
   425 	});
       
   426 
       
   427 	ps.subscribe( 'switchedMode', function( from, to ) {
       
   428 		api.refresh_buttons(true);
       
   429 
       
   430 		if ( to === 'html' )
       
   431 			setTimeout( api.resize_textarea, 200 );
       
   432 	});
       
   433 
       
   434 	/**
       
   435 	 * Buttons
       
   436 	 */
       
   437 	api.b = function() {
       
   438 		if ( s.has_tinymce && 'tinymce' === s.mode )
       
   439 			tinyMCE.execCommand('Bold');
       
   440 	}
       
   441 
       
   442 	api.i = function() {
       
   443 		if ( s.has_tinymce && 'tinymce' === s.mode )
       
   444 			tinyMCE.execCommand('Italic');
       
   445 	}
       
   446 
       
   447 	api.ul = function() {
       
   448 		if ( s.has_tinymce && 'tinymce' === s.mode )
       
   449 			tinyMCE.execCommand('InsertUnorderedList');
       
   450 	}
       
   451 
       
   452 	api.ol = function() {
       
   453 		if ( s.has_tinymce && 'tinymce' === s.mode )
       
   454 			tinyMCE.execCommand('InsertOrderedList');
       
   455 	}
       
   456 
       
   457 	api.link = function() {
       
   458 		if ( s.has_tinymce && 'tinymce' === s.mode )
       
   459 			tinyMCE.execCommand('WP_Link');
       
   460 		else
       
   461 			wpLink.open();
       
   462 	}
       
   463 
       
   464 	api.unlink = function() {
       
   465 		if ( s.has_tinymce && 'tinymce' === s.mode )
       
   466 			tinyMCE.execCommand('unlink');
       
   467 	}
       
   468 
       
   469 	api.atd = function() {
       
   470 		if ( s.has_tinymce && 'tinymce' === s.mode )
       
   471 			tinyMCE.execCommand('mceWritingImprovementTool');
       
   472 	}
       
   473 
       
   474 	api.help = function() {
       
   475 		if ( s.has_tinymce && 'tinymce' === s.mode )
       
   476 			tinyMCE.execCommand('WP_Help');
       
   477 	}
       
   478 
       
   479 	api.blockquote = function() {
       
   480 		if ( s.has_tinymce && 'tinymce' === s.mode )
       
   481 			tinyMCE.execCommand('mceBlockQuote');
       
   482 	}
       
   483 
       
   484 	api.medialib = function() {
       
   485 		if ( s.has_tinymce && 'tinymce' === s.mode ) {
       
   486 			tinyMCE.execCommand('WP_Medialib');
       
   487 		} else {
       
   488 			var href = $('#wp-' + s.editor_id + '-media-buttons a.thickbox').attr('href') || '';
       
   489 
       
   490 			if ( href )
       
   491 				tb_show('', href);
       
   492 		}
       
   493 	}
       
   494 
       
   495 	api.refresh_buttons = function( fade ) {
       
   496 		fade = fade || false;
       
   497 
       
   498 		if ( s.mode === 'html' ) {
       
   499 			$('#wp-fullscreen-mode-bar').removeClass('wp-tmce-mode').addClass('wp-html-mode');
       
   500 
       
   501 			if ( fade )
       
   502 				$('#wp-fullscreen-button-bar').fadeOut( 150, function(){
       
   503 					$(this).addClass('wp-html-mode').fadeIn( 150 );
       
   504 				});
       
   505 			else
       
   506 				$('#wp-fullscreen-button-bar').addClass('wp-html-mode');
       
   507 
       
   508 		} else if ( s.mode === 'tinymce' ) {
       
   509 			$('#wp-fullscreen-mode-bar').removeClass('wp-html-mode').addClass('wp-tmce-mode');
       
   510 
       
   511 			if ( fade )
       
   512 				$('#wp-fullscreen-button-bar').fadeOut( 150, function(){
       
   513 					$(this).removeClass('wp-html-mode').fadeIn( 150 );
       
   514 				});
       
   515 			else
       
   516 				$('#wp-fullscreen-button-bar').removeClass('wp-html-mode');
       
   517 		}
       
   518 	}
       
   519 
       
   520 	/**
       
   521 	 * UI Elements
       
   522 	 *
       
   523 	 * Used for transitioning between states.
       
   524 	 */
       
   525 	api.ui = {
       
   526 		init: function() {
       
   527 			var topbar = $('#fullscreen-topbar'), txtarea = $('#wp_mce_fullscreen'), last = 0;
       
   528 
       
   529 			s.toolbars = topbar.add( $('#wp-fullscreen-status') );
       
   530 			s.element = $('#fullscreen-fader');
       
   531 			s.textarea_obj = txtarea[0];
       
   532 			s.has_tinymce = typeof(tinymce) != 'undefined';
       
   533 
       
   534 			if ( !s.has_tinymce )
       
   535 				$('#wp-fullscreen-mode-bar').hide();
       
   536 
       
   537 			if ( wptitlehint && $('#wp-fullscreen-title').length )
       
   538 				wptitlehint('wp-fullscreen-title');
       
   539 
       
   540 			$(document).keyup(function(e){
       
   541 				var c = e.keyCode || e.charCode, a, data;
       
   542 
       
   543 				if ( !fullscreen.settings.visible )
       
   544 					return true;
       
   545 
       
   546 				if ( navigator.platform && navigator.platform.indexOf('Mac') != -1 )
       
   547 					a = e.ctrlKey; // Ctrl key for Mac
       
   548 				else
       
   549 					a = e.altKey; // Alt key for Win & Linux
       
   550 
       
   551 				if ( 27 == c ) { // Esc
       
   552 					data = {
       
   553 						event: e,
       
   554 						what: 'dfw',
       
   555 						cb: fullscreen.off,
       
   556 						condition: function(){
       
   557 							if ( $('#TB_window').is(':visible') || $('.wp-dialog').is(':visible') )
       
   558 								return false;
       
   559 							return true;
       
   560 						}
       
   561 					};
       
   562 
       
   563 					if ( ! jQuery(document).triggerHandler( 'wp_CloseOnEscape', [data] ) )
       
   564 						fullscreen.off();
       
   565 				}
       
   566 
       
   567 				if ( a && (61 == c || 107 == c || 187 == c) ) // +
       
   568 					api.dfw_width(25);
       
   569 
       
   570 				if ( a && (45 == c || 109 == c || 189 == c) ) // -
       
   571 					api.dfw_width(-25);
       
   572 
       
   573 				if ( a && 48 == c ) // 0
       
   574 					api.dfw_width(0);
       
   575 
       
   576 				return false;
       
   577 			});
       
   578 
       
   579 			// word count in HTML mode
       
   580 			if ( typeof(wpWordCount) != 'undefined' ) {
       
   581 
       
   582 				txtarea.keyup( function(e) {
       
   583 					var k = e.keyCode || e.charCode;
       
   584 
       
   585 					if ( k == last )
       
   586 						return true;
       
   587 
       
   588 					if ( 13 == k || 8 == last || 46 == last )
       
   589 						$(document).triggerHandler('wpcountwords', [ txtarea.val() ]);
       
   590 
       
   591 					last = k;
       
   592 					return true;
       
   593 				});
       
   594 			}
       
   595 
       
   596 			topbar.mouseenter(function(e){
       
   597 				s.toolbars.addClass('fullscreen-make-sticky');
       
   598 				$( document ).unbind( '.fullscreen' );
       
   599 				clearTimeout( s.timer );
       
   600 				s.timer = 0;
       
   601 			}).mouseleave(function(e){
       
   602 				s.toolbars.removeClass('fullscreen-make-sticky');
       
   603 
       
   604 				if ( s.visible )
       
   605 					$( document ).bind( 'mousemove.fullscreen', function(e) { bounder( 'showToolbar', 'hideToolbar', 2000, e ); } );
       
   606 			});
       
   607 		},
       
   608 
       
   609 		fade: function( before, during, after ) {
       
   610 			if ( ! s.element )
       
   611 				api.ui.init();
       
   612 
       
   613 			// If any callback bound to before returns false, bail.
       
   614 			if ( before && ! ps.publish( before ) )
       
   615 				return;
       
   616 
       
   617 			api.fade.In( s.element, 600, function() {
       
   618 				if ( during )
       
   619 					ps.publish( during );
       
   620 
       
   621 				api.fade.Out( s.element, 600, function() {
       
   622 					if ( after )
       
   623 						ps.publish( after );
       
   624 				})
       
   625 			});
       
   626 		}
       
   627 	};
       
   628 
       
   629 	api.fade = {
       
   630 		transitionend: 'transitionend webkitTransitionEnd oTransitionEnd',
       
   631 
       
   632 		// Sensitivity to allow browsers to render the blank element before animating.
       
   633 		sensitivity: 100,
       
   634 
       
   635 		In: function( element, speed, callback, stop ) {
       
   636 
       
   637 			callback = callback || $.noop;
       
   638 			speed = speed || 400;
       
   639 			stop = stop || false;
       
   640 
       
   641 			if ( api.fade.transitions ) {
       
   642 				if ( element.is(':visible') ) {
       
   643 					element.addClass( 'fade-trigger' );
       
   644 					return element;
       
   645 				}
       
   646 
       
   647 				element.show();
       
   648 				element.first().one( this.transitionend, function() {
       
   649 					callback();
       
   650 				});
       
   651 				setTimeout( function() { element.addClass( 'fade-trigger' ); }, this.sensitivity );
       
   652 			} else {
       
   653 				if ( stop )
       
   654 					element.stop();
       
   655 
       
   656 				element.css( 'opacity', 1 );
       
   657 				element.first().fadeIn( speed, callback );
       
   658 
       
   659 				if ( element.length > 1 )
       
   660 					element.not(':first').fadeIn( speed );
       
   661 			}
       
   662 
       
   663 			return element;
       
   664 		},
       
   665 
       
   666 		Out: function( element, speed, callback, stop ) {
       
   667 
       
   668 			callback = callback || $.noop;
       
   669 			speed = speed || 400;
       
   670 			stop = stop || false;
       
   671 
       
   672 			if ( ! element.is(':visible') )
       
   673 				return element;
       
   674 
       
   675 			if ( api.fade.transitions ) {
       
   676 				element.first().one( api.fade.transitionend, function() {
       
   677 					if ( element.hasClass('fade-trigger') )
       
   678 						return;
       
   679 
       
   680 					element.hide();
       
   681 					callback();
       
   682 				});
       
   683 				setTimeout( function() { element.removeClass( 'fade-trigger' ); }, this.sensitivity );
       
   684 			} else {
       
   685 				if ( stop )
       
   686 					element.stop();
       
   687 
       
   688 				element.first().fadeOut( speed, callback );
       
   689 
       
   690 				if ( element.length > 1 )
       
   691 					element.not(':first').fadeOut( speed );
       
   692 			}
       
   693 
       
   694 			return element;
       
   695 		},
       
   696 
       
   697 		transitions: (function() { // Check if the browser supports CSS 3.0 transitions
       
   698 			var s = document.documentElement.style;
       
   699 
       
   700 			return ( typeof ( s.WebkitTransition ) == 'string' ||
       
   701 				typeof ( s.MozTransition ) == 'string' ||
       
   702 				typeof ( s.OTransition ) == 'string' ||
       
   703 				typeof ( s.transition ) == 'string' );
       
   704 		})()
       
   705 	};
       
   706 
       
   707 	/**
       
   708 	 * Resize API
       
   709 	 *
       
   710 	 * Automatically updates textarea height.
       
   711 	 */
       
   712 
       
   713 	api.bind_resize = function() {
       
   714 		$(s.textarea_obj).bind('keypress.grow click.grow paste.grow', function(){
       
   715 			setTimeout( api.resize_textarea, 200 );
       
   716 		});
       
   717 	}
       
   718 
       
   719 	api.oldheight = 0;
       
   720 	api.resize_textarea = function() {
       
   721 		var txt = s.textarea_obj, newheight;
       
   722 
       
   723 		newheight = txt.scrollHeight > 300 ? txt.scrollHeight : 300;
       
   724 
       
   725 		if ( newheight != api.oldheight ) {
       
   726 			txt.style.height = newheight + 'px';
       
   727 			api.oldheight = newheight;
       
   728 		}
       
   729 	};
       
   730 
       
   731 })(jQuery);