<commit_before>var admin = {

		/**
		 *
		 * @param opts
		 * 		title
		 * 		html
		 * 		loaded
		 * 		buttons: []
		 * 			text
		 * 			href
		 * 			click
		 * 			action: 'close'
		 * 			addclass
		 */
		dialog: function(opts){
			var dialog =  $('
').addClass('modal hide fade').attr('id', 'modal')
				.attr({'role':'dialog'})
				.append(
						(opts.title ? '

' + opts.title + '

' : ''),
						'
' + ( opts.html ? opts.html : '') + '
'
						);
			if (opts.buttons && typeof opts.buttons == 'object'){

				var footer = $('
').addClass('modal-footer');

				dialog.append(footer);

				$.each(opts.buttons, function(index, but){
					var a = $('').addClass('btn').html(but.text);

					if (but.href){
						a.attr('href', but.href);
					}

					if (but.click){
						a.click(function(){ but.click(); });
					}

					if (but.action == 'close'){
						a.attr('data-dismiss', 'modal');
						dialog.on('hidden', function(){
							dialog.remove();
						});
					}

					if(but.addclass){
						a.addClass(but.addclass);
					}

					footer.append(a);
				});

			}
			dialog.modal();

			if (opts.html && opts.loaded){
				dialog.on('shown', function(){;
					opts.loaded(dialog);
				})
			}

			if (opts.obj && opts.method){
				dialog.find('.modal-body').html('');
				$.ajax({
	        		'type': opts.post ? 'POST' : 'GET',
	        		'url': 'controller.php?obj=' + opts.obj + '&method=' + opts.method + (opts.param ? '&param[]=' + opts.param.join('&param[]=') : '' ),
	        		'data': opts.post
	        	})
	        	.done(function(data){
	        		dialog.find('.modal-body').html(data);
	        		if (opts.loaded){
	        			opts.loaded(dialog);
	        		}
	        	});
			}
		},
		/**
		 *
		 * @param text
		 * @param title
		 * @param type : false, info, success, error
		 * @param sticky
		 */
		message: function(text, type, title, sticky){
		    $.pnotify({
		        title: title ?  title : false,
		        text: text,
		        type: type ? type : false,
		        hide: sticky ? false : true
		    });
		},

	tabs : {
	    tab: '',
	    set: function(el){
	        if (typeof el == 'string'){
	            this.tab = $(el);
	        } else {
	            this.tab = el;
	        }
	    },
	    start: function(){
	        tab = this.tab;
	        tab.find('a').click(function (e) {
	            e.preventDefault();
	            $(this).tab('show');
	        });

	        tab.find('button.close').click(function(e){
	            var li = $(this).parents('li');
	            admin.tabs.closeTab(li);
	            return false;
	        });
	    },
	    /**
	     *
	     * @param opts object
	     * 		title
	     *
	     * 		html
	     *
	     * 		obj
	     * 		method
	     */
	    add: function(opts){

	    	var title = opts.title ? opts.title : '',
	    	tab = this.tab,
	    	id = Math.floor(Math.random()*1000) + '' + tab.find('li').length;

	        this.tab.append('
' + title + '<button class="close" type="button">×</button>
');

	        if (opts.html){
	        	this.tab.next('div.tab-content').append('
' + opts.html + '
');
	        	this.start(tab);
        		this.tab.find('li a:last').tab('show');
	        } else if (opts.obj && opts.method){
	        	admin.tabs.tab.next('div.tab-content')
    				.append('

');
	        	admin.tabs.start(tab);
        		admin.tabs.tab.find('li a:last').data('opts', opts).tab('show');
	        	$.ajax({
	        		'type': opts.post ? 'POST' : 'GET',
	        		'url': 'controller.php?obj=' + opts.obj + '&method=' + opts.method + (opts.param ? '&param[]=' + opts.param.join('&param[]=') : '' ),
	        		'data': opts.post
	        	})
	        	.done(function(data){
	        		$('#added' + id).html(data);
	        	});
	        } else {
	        	return false;
	        }
	    },

	    reloadActive: function(){
	    	var d_active = $('div.tab-content div.active'),
	    	opts = tab.find('li.active a').data('opts');

	    	var url = 'controller.php?obj=' + opts.obj + '&method=' + opts.method + (opts.param ? '&param[]=' + opts.param.join('&param[]=') : '' );

	    	d_active.html('').load(url, opts.data);
	    },

	    closeActive: function(state){
	    	var active = tab.find('li.active');
	    	admin.tabs.closeTab(active, state);
	    },

	    closeTab: function(li, state){
	    	$('#' + li.find('a').attr('href').replace('#', '')).remove();
            if (li.hasClass('active')){
            	if (state){
            		var actualState = $.bbq.getState();
                	if (actualState[state] == ''){
                		$(window).trigger( "hashchange" );
                	} else {
                		$.bbq.pushState('#' + state);
                		tab.find('li a:last').tab('show');
                	}
                	li.remove();
                } else {
                	li.remove();
                	$.bbq.pushState('#');
                	tab.find('li a:last').tab('show');
                }
            } else {
            	li.remove();
            }
	    }
	},

	tr: function (string) {

		return lang[string] ? lang[string] : string;
	},

	media: {
		go2dir: function(dir){
			admin.tabs.closeActive('media/all/' + dir);
		},

		deleteDir: function(path){
			$.get('controller.php?obj=media_ctrl&method=delete&param[]=' + path, function(data){
				admin.message(data.text, data.status);
				if(data.status == 'success'){
					admin.media.go2dir(data.new_path);
				}
			}, 'json');
		},

		deleteFile: function(full_path){
			admin.dialog({
				title: admin.tr('pay_attention_please'),
				html: admin.tr('confirm_delete_file'),
				buttons:[
				         {
				        	 text: admin.tr('close'),
				        	 action: 'close'
				         },
				         {
				        	 text: admin.tr('delete'),
				        	 addclass: 'btn-danger',
				        	 action: 'close',
				        	 click: function(){
				        		 $.get('controller.php?obj=media_ctrl&method=delete&param[]=' + full_path, function(data){
				        			 admin.message(data.text, data.status);
				        			 if (data.status == 'success'){
				        				 admin.tabs.closeActive('media/all/' + data.new_path);
				        			 }
				        		 }, 'json')
				        	 }
				         }
				         ]
			});

		},

		filterDir: function(filter, uid){
			if (filter) {
		      $('#media-' + uid + ' div.searcheable:not(:Contains(' + filter + '))').parents('li').fadeOut();
		      $('#media-' + uid + ' div.searcheable:Contains(' + filter + ')').parents('li').fadeIn();
		    } else {
		      $('#media-' + uid + ' li').fadeIn();
		    }
		}
	}
};

$(function () {
	admin.tabs.set('#tabs');
	admin.tabs.start();

    $(window).on( 'hashchange', function(e) {
    	var url = $.param.fragment();

    	if (!url){
    		return;
    	} else if (url.match(/nt-/)){
			return;
    	} else {
    		var url_arr = url.split('/');

    		admin.tabs.add({
    			'title': url_arr[0] + '/' + url_arr[1] + (url_arr[2] ? '/' + url_arr[2].substr(0, 5) + '...' : ''),
    			'obj': url_arr[0] + '_ctrl',
    			'method': url_arr[1],
    			'param': url_arr.slice(2)
    		});
    	}
    }).trigger('hashchange');

    tinyMCE.init({
    	// General options
    	mode : "exact",
    	theme : 'advanced',
    	plugins : 'pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,advlist',

    	// Theme options
    	theme_advanced_toolbar_location : 'top',
    	theme_advanced_toolbar_align : 'left',
    	extended_valid_elements : 'script[language|type]',
    	theme_advanced_resizing : true,
    	content_css : './css/bootstrap.min.css,./sites/default/css/styles.css',

    	theme_advanced_statusbar_location : 'bottom',

    	theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,formatselect,fontselect,fontsizeselect",
    	theme_advanced_buttons2 : "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,",
    	theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup",
    	theme_advanced_buttons4 : "charmap,iespell,media,|,ltr,rtl,|,fullscreen,|,attribs,|,link,unlink,anchor,image,cleanup,code,|,preview,|,forecolor,backcolor",

    	forced_root_block: '',
    	force_p_newlines: true
	});
});

jQuery.expr[':'].Contains = function(a,i,m){
    return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
<commit_msg>TinyMCE accepts DIV insertion<commit_after>var admin = {

		/**
		 *
		 * @param opts
		 * 		title
		 * 		html
		 * 		loaded
		 * 		buttons: []
		 * 			text
		 * 			href
		 * 			click
		 * 			action: 'close'
		 * 			addclass
		 */
		dialog: function(opts){
			var dialog =  $('
').addClass('modal hide fade').attr('id', 'modal')
				.attr({'role':'dialog'})
				.append(
						(opts.title ? '

' + opts.title + '

' : ''),
						'
' + ( opts.html ? opts.html : '') + '
'
						);
			if (opts.buttons && typeof opts.buttons == 'object'){

				var footer = $('
').addClass('modal-footer');

				dialog.append(footer);

				$.each(opts.buttons, function(index, but){
					var a = $('').addClass('btn').html(but.text);

					if (but.href){
						a.attr('href', but.href);
					}

					if (but.click){
						a.click(function(){ but.click(); });
					}

					if (but.action == 'close'){
						a.attr('data-dismiss', 'modal');
						dialog.on('hidden', function(){
							dialog.remove();
						});
					}

					if(but.addclass){
						a.addClass(but.addclass);
					}

					footer.append(a);
				});

			}
			dialog.modal();

			if (opts.html && opts.loaded){
				dialog.on('shown', function(){;
					opts.loaded(dialog);
				})
			}

			if (opts.obj && opts.method){
				dialog.find('.modal-body').html('');
				$.ajax({
	        		'type': opts.post ? 'POST' : 'GET',
	        		'url': 'controller.php?obj=' + opts.obj + '&method=' + opts.method + (opts.param ? '&param[]=' + opts.param.join('&param[]=') : '' ),
	        		'data': opts.post
	        	})
	        	.done(function(data){
	        		dialog.find('.modal-body').html(data);
	        		if (opts.loaded){
	        			opts.loaded(dialog);
	        		}
	        	});
			}
		},
		/**
		 *
		 * @param text
		 * @param title
		 * @param type : false, info, success, error
		 * @param sticky
		 */
		message: function(text, type, title, sticky){
		    $.pnotify({
		        title: title ?  title : false,
		        text: text,
		        type: type ? type : false,
		        hide: sticky ? false : true
		    });
		},

	tabs : {
	    tab: '',
	    set: function(el){
	        if (typeof el == 'string'){
	            this.tab = $(el);
	        } else {
	            this.tab = el;
	        }
	    },
	    start: function(){
	        tab = this.tab;
	        tab.find('a').click(function (e) {
	            e.preventDefault();
	            $(this).tab('show');
	        });

	        tab.find('button.close').click(function(e){
	            var li = $(this).parents('li');
	            admin.tabs.closeTab(li);
	            return false;
	        });
	    },
	    /**
	     *
	     * @param opts object
	     * 		title
	     *
	     * 		html
	     *
	     * 		obj
	     * 		method
	     */
	    add: function(opts){

	    	var title = opts.title ? opts.title : '',
	    	tab = this.tab,
	    	id = Math.floor(Math.random()*1000) + '' + tab.find('li').length;

	        this.tab.append('
' + title + '<button class="close" type="button">×</button>
');

	        if (opts.html){
	        	this.tab.next('div.tab-content').append('
' + opts.html + '
');
	        	this.start(tab);
        		this.tab.find('li a:last').tab('show');
	        } else if (opts.obj && opts.method){
	        	admin.tabs.tab.next('div.tab-content')
    				.append('

');
	        	admin.tabs.start(tab);
        		admin.tabs.tab.find('li a:last').data('opts', opts).tab('show');
	        	$.ajax({
	        		'type': opts.post ? 'POST' : 'GET',
	        		'url': 'controller.php?obj=' + opts.obj + '&method=' + opts.method + (opts.param ? '&param[]=' + opts.param.join('&param[]=') : '' ),
	        		'data': opts.post
	        	})
	        	.done(function(data){
	        		$('#added' + id).html(data);
	        	});
	        } else {
	        	return false;
	        }
	    },

	    reloadActive: function(){
	    	var d_active = $('div.tab-content div.active'),
	    	opts = tab.find('li.active a').data('opts');

	    	var url = 'controller.php?obj=' + opts.obj + '&method=' + opts.method + (opts.param ? '&param[]=' + opts.param.join('&param[]=') : '' );

	    	d_active.html('').load(url, opts.data);
	    },

	    closeActive: function(state){
	    	var active = tab.find('li.active');
	    	admin.tabs.closeTab(active, state);
	    },

	    closeTab: function(li, state){
	    	$('#' + li.find('a').attr('href').replace('#', '')).remove();
            if (li.hasClass('active')){
            	if (state){
            		var actualState = $.bbq.getState();
                	if (actualState[state] == ''){
                		$(window).trigger( "hashchange" );
                	} else {
                		$.bbq.pushState('#' + state);
                		tab.find('li a:last').tab('show');
                	}
                	li.remove();
                } else {
                	li.remove();
                	$.bbq.pushState('#');
                	tab.find('li a:last').tab('show');
                }
            } else {
            	li.remove();
            }
	    }
	},

	tr: function (string) {

		return lang[string] ? lang[string] : string;
	},

	media: {
		go2dir: function(dir){
			admin.tabs.closeActive('media/all/' + dir);
		},

		deleteDir: function(path){
			$.get('controller.php?obj=media_ctrl&method=delete&param[]=' + path, function(data){
				admin.message(data.text, data.status);
				if(data.status == 'success'){
					admin.media.go2dir(data.new_path);
				}
			}, 'json');
		},

		deleteFile: function(full_path){
			admin.dialog({
				title: admin.tr('pay_attention_please'),
				html: admin.tr('confirm_delete_file'),
				buttons:[
				         {
				        	 text: admin.tr('close'),
				        	 action: 'close'
				         },
				         {
				        	 text: admin.tr('delete'),
				        	 addclass: 'btn-danger',
				        	 action: 'close',
				        	 click: function(){
				        		 $.get('controller.php?obj=media_ctrl&method=delete&param[]=' + full_path, function(data){
				        			 admin.message(data.text, data.status);
				        			 if (data.status == 'success'){
				        				 admin.tabs.closeActive('media/all/' + data.new_path);
				        			 }
				        		 }, 'json')
				        	 }
				         }
				         ]
			});

		},

		filterDir: function(filter, uid){
			if (filter) {
		      $('#media-' + uid + ' div.searcheable:not(:Contains(' + filter + '))').parents('li').fadeOut();
		      $('#media-' + uid + ' div.searcheable:Contains(' + filter + ')').parents('li').fadeIn();
		    } else {
		      $('#media-' + uid + ' li').fadeIn();
		    }
		}
	}
};

$(function () {
	admin.tabs.set('#tabs');
	admin.tabs.start();

    $(window).on( 'hashchange', function(e) {
    	var url = $.param.fragment();

    	if (!url){
    		return;
    	} else if (url.match(/nt-/)){
			return;
    	} else {
    		var url_arr = url.split('/');

    		admin.tabs.add({
    			'title': url_arr[0] + '/' + url_arr[1] + (url_arr[2] ? '/' + url_arr[2].substr(0, 5) + '...' : ''),
    			'obj': url_arr[0] + '_ctrl',
    			'method': url_arr[1],
    			'param': url_arr.slice(2)
    		});
    	}
    }).trigger('hashchange');

    tinyMCE.init({
    	// General options
    	mode : "exact",
    	theme : 'advanced',
    	plugins : 'pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,advlist',

    	// Theme options
    	theme_advanced_toolbar_location : 'top',
    	theme_advanced_toolbar_align : 'left',
    	extended_valid_elements : 'script[language|type]',
    	theme_advanced_resizing : true,
    	content_css : './css/bootstrap.min.css,./sites/default/css/styles.css',

    	theme_advanced_statusbar_location : 'bottom',

    	theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,formatselect,fontselect,fontsizeselect",
    	theme_advanced_buttons2 : "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote|,undo,redo,",
    	theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup",
    	theme_advanced_buttons4 : "charmap,iespell,media,|,ltr,rtl,|,fullscreen,|,attribs,|,link,unlink,anchor,image,cleanup,code,|,preview,|,forecolor,backcolor",

    	forced_root_block: '',
    	force_p_newlines: true,
    	theme_advanced_blockformats: "p,address,pre,h1,h2,h3,h4,h5,h6,div"
	});
});

jQuery.expr[':'].Contains = function(a,i,m){
    return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};

$('a.ftpopover').popover({html:true});
<|endoftext|>


<commit_before>/**
 * WP Present - admin.js
 *
 */

var WPPresentAdmin;
(function($) {

	WPPresentAdmin = function() {
		this.init();
		return this;
	}

	WPPresentAdmin.prototype = {
		init: function() {
			var self = this;

			// Make the outer container resizeable
			$( "#outer-container" ).resizable();

			$( "#container" ).sortable({
				stop: function( event, ui ) {
					self.updateTaxonomyDescription();
				}
			});

			$( ".column-inner" ).sortable({
				connectWith: ".column-inner",
				stop: function( event, ui ) {
					self.updateTaxonomyDescription();
				}
			});

			self.widgetButtonExpand();
			self.widgetButtonEdit();
			self.widgetButtonDelete();
			self.widgetButtonAdd();
			self.widgetButtonTidy();
			self.bindButtonAddColumn();
			self.bindButtonViewPresentation();
			self.backfillColumns();
			self.refreshUI();

			// Select the first column on load
			self.activateColumn( $('#col-1').children('.widget-top').children('.widget-title') );

			$('.spinner').hide();

			return self;
		},

		/**
		 * Sortables and such
		 */
		refreshUI: function () {
			var self = this;

			$( "#container" ).sortable("refresh");

			// Append an inner column to each column that doesn't contain any slides.
			$('.column' ).not(":has(div.column-inner)").append('

');

			$( ".column-inner" ).sortable({
				connectWith: ".column-inner",
				stop: function( event, ui ) {
					self.updateTaxonomyDescription();
				}
			});

			// This really should be called only once inside init
			self.enableColumns();
		},

		/**
		 * Backfill Columns
		 */
		backfillColumns: function () {
			var self = this;
			var numSlides = WPPNumSlides[0];
			var numExisting = $('#container > .column').size();

			for (var col=numExisting+1;col<=numSlides;col++){
				self.addColumn();
			}
			//$('#container').append( '

' );
		},

		/**
		 * Make the column the target for adding new columns
		 */
		activateColumn: function ( $col ) {
			// Remove the active class from all columns
			$('.widget-title').css('background', '');
			$('.widget-title').css('color', '');
			$('.widget-title').removeClass('active');

			// Select the given column
			$col.css('background-color', '#2ea2cc').css('color','#ffffff');
			$col.addClass('active');
		},

		/**
		 * Encode,update, and save the columns data into the hidden taxonomy description field
		 */
		updateTaxonomyDescription: function () {
			var self = this;
			var columns = { }; // Creates a new object
			var i = 1;
			$( '.column-inner' ).each(function( index ) {
				var $order = $(this).sortable( "toArray" );
				columns['col-'+i] = $order;
				i++;
			});
			var encoded = JSON.stringify( columns );
			$('#description').val(encoded);

			// This was a convient time to do this UI clean-up
			self.renumberColumns();

			// Send this change off to ajax land
			self.updatePresentation();
		},

		/**
		 * AJAX request to update the current presentation taxonomy
		 */
		updatePresentation: function () {
			var nonce = $('#wp-present-nonce').val();
			var params = { 'content':$('#description').val(), 'id':presentation, 'nonce':nonce };
			$.ajax({
				url: ajaxurl + '?action=update_presentation',
				type: 'POST',
				data: jQuery.param(params),
				beforeSend: function( xhr ) {
					$('.spinner').show();
				},
				complete: function( xhr ) {
					//$('.spinner').hide();
				},
				success: function(result) {
					$('.spinner').hide();
					// Return the excerpt from the editor
					//console.log('presentation updated');
				}
			});
		},

		/**
		 * Bind Tidy button
		 */
		widgetButtonTidy: function () {
			var self = this;
			$('.action-buttons').on('click', '#tidy-button', function(e) {
				e.preventDefault();
				this.consolidateColumns();
				// Why does this break?
				//updatePresentation();
			});
		},

		/**
		 * Expand slide details
		 */
		widgetButtonExpand: function () {
			$('#container').on('click', '.widget-title-action', function(e) {
				$( this ).parents('.widget').children('.widget-inside').toggle();
			});
		},

		/**
		 * The action taken by the Tidy button
		 */
		consolidateColumns: function () {
			var self = this;
			var numCols = $('.column').length;
			// console.log(numCols);

			$('.column').each(function(outerIndex){

				// Don't have a col 0
				var outerIndex = outerIndex + 1;

				// Fixes the condition where we are looking at the last item
				if( outerIndex >= numCols )
					return;

				var $outerCol = $(this);
				var $innerCol = $outerCol.children('.column-inner');

				var innerHTML = $innerCol.html().trim();
				var outerHTML = $outerCol.html().trim();

				if( typeof(innerHTML) !== 'string' || '' == innerHTML ) {
					var $nextOuterCol = $('#col-'+(outerIndex+1));
					var $nextInnerCol = $nextOuterCol.children('.column-inner');

					var nextInnerHTML = $nextInnerCol.html().trim();
					var nextOuterHTML = $nextOuterCol.html().trim();

					$outerCol.html(nextOuterHTML);
					$nextOuterCol.html(outerHTML);
				}
			} );

			//Finally refresh description array
			$( ".column-inner" ).sortable({
				connectWith: ".column-inner",
				stop: (function( event, ui ) {
					self.updateTaxonomyDescription();
				}),
				create: (function( event, ui ) {
					self.updateTaxonomyDescription();
				})
			});
			self.updateTaxonomyDescription();
		},

		/**
		 * Setup the column to allow for activation
		 */
		enableColumns: function () {
			var self = this;
			$('.column').children('.widget-top').on('click', '.widget-title', function(e) {
				$col = $(this);
				self.activateColumn($col);
			});
		},

		// Bind Edit button
		widgetButtonEdit: function () {
			var self = this;
			$('#container').on('click', '.widget-control-edit', function(e) {
				e.preventDefault();
				var $button = $(this);
				var $parentWidget = $button.parents('.widget');
				var $widgetPreview = $parentWidget.find('.widget-preview');

				// Send the contents from the widget to the editor
				var contentEditor  = $widgetPreview.html();
				var widgetID = $parentWidget.find('.slide-id').val();
				var nonce = $('#wp-present-nonce').val();

				$('#editor_slide-tmce').click(); //Necessary on subsequent loads of the editor
				$( "#dialog" ).dialog({
				  autoOpen: true,
				  width: 640,
				  height: 640,
				  modal: false,
				  buttons: {
					"Update": function() {
						var editorContents = tinymce.get('editor_slide').getContent();
						var params = { 'content':editorContents, 'id':widgetID, 'nonce':nonce };
						// Send the contents of the existing post
						$.ajax({
							url: ajaxurl + '?action=update_slide',
							type: 'POST',
							data: jQuery.param(params),
							beforeSend: function( xhr ) {
								$('.spinner').show();
							},
							complete: function( xhr ) {
								//$('.spinner').hide();
							},
							success: function(result) {
								$('.spinner').hide();
								// Return the excerpt from the editor
								$widgetPreview.html( result );
						  }
						});
						self.closeModal();
					},
					Cancel: function() {
						self.closeModal();
					}
				  },
				  create: function() {
						tinymce.execCommand('mceRemoveControl',true,'editor_slide');
						tinymce.execCommand('mceAddControl',true,'editor_slide');
				  },
				  open: function() {
						// Load the contents of the existing post
						var nonce = $('#wp-present-nonce').val();
						var params = { 'id':widgetID, 'nonce':nonce };
						$.ajax({
							url: ajaxurl + '?action=get_slide',
							data: jQuery.param(params),
							beforeSend: function( xhr ) {
								$('.spinner').show();
							},
							complete: function( xhr ) {
								$('.spinner').hide();
							},
							success: function(contentEditor) {
								tinymce.get('editor_slide').setContent(contentEditor);
							}
						});

						// Hack for getting the reveal class added to tinymce editor body
						// @todo: look at wp_editor in wp/inc/class-wp-editor.php
						var $editorIframe = $('#editor_slide_ifr').contents();
						$editorIframe.find('body').addClass('reveal');
						$editorIframe.css('height','500px');

				  },
				  close: function() {
						self.closeModal();
				  }
				});
			});
		},

		// Bind Delete button
		widgetButtonDelete: function () {
			var self = this;
			$('#container').on('click', '.widget-control-remove', function(e) {
				e.preventDefault();

				var confirmDelete = confirm("You are about to permanently delete the selected slide. 'Cancel' to stop, 'OK' to delete.");
				if( false == confirmDelete )
					return;

				var $button = $(this);
				var $parentWidget = $button.parents('.widget');
				var widgetID = $parentWidget.find('.slide-id').val();
				var nonce = $('#wp-present-nonce').val();
				var params = { 'id':widgetID, 'nonce':nonce };

				$.ajax({
					url: ajaxurl + '?action=delete_slide',
					type: 'POST',
					data: jQuery.param(params),
					beforeSend: function( xhr ) {
						$('.spinner').show();
					},
					complete: function( xhr ) {
						//$('.spinner').hide();
					},
					success: function(result) {
						$('.spinner').hide();
						$parentWidget.remove();
						self.updateTaxonomyDescription();
					}
				});
			});
		},

		// Bind Add button
		widgetButtonAdd: function () {
			var self = this;
			$('.action-buttons').on('click', '#add-button', function(e) {
				e.preventDefault();
				var $button = $(this);
				var $activeColumn = $('.widget-title.active').parent('.widget-top').parent('.column').children('.column-inner');

				$('#editor_slide-tmce').click(); //Necessary on subsequent loads of the editor
				$( "#dialog" ).dialog({
				  autoOpen: true,
				  width: 600,
				  height: 600,
				  modal: false,
				  buttons: {
					"Publish": function() {
						var editorContents = tinymce.get('editor_slide').getContent();
						var nonce = $('#wp-present-nonce').val();
						var params = { content:editorContents,'presentation':presentation, 'nonce':nonce };
						// Send the contents of the existing post
						$.ajax({
							url: ajaxurl + '?action=new_slide',
							type: 'POST',
							data: jQuery.param(params),
							success: function(result) {
								$activeColumn.append(result);
								WPPNumSlides[0]++;
								self.addColumn();
								self.refreshUI();
								self.updateTaxonomyDescription();
						  }
						});
						self.closeModal();
					},
					Cancel: function() {
						self.closeModal();
					}
				  },
				  create: function() {
						tinymce.execCommand('mceRemoveControl',true,'editor_slide');
						tinymce.execCommand('mceAddControl',true,'editor_slide');
				  },
				  open: function() {
						// Clear the editor
						tinymce.get('editor_slide').setContent('');
						// Hack for getting the reveal class added to tinymce editor body
						var $editorIframe = $('#editor_slide_ifr').contents();
						$editorIframe.find('body').addClass('reveal');
				  },
				  close: function() {
						self.closeModal();
				  }
				});
			});
		},

		// Adds a column to the presentation
		addColumn: function () {
			var self = this;
			var col = $('#container > .column').size() + 1;

			$('#container').append( '

'+col+'

' );
			$('#container').width( $('#container').width() + 210 );
		},

		//Bind Add Column button to addColumn()
		bindButtonAddColumn: function () {
			var self = this;
			$('.action-buttons').on('click', '#add-column', function(e) {
				e.preventDefault();
				self.addColumn();
				self.refreshUI();
			});
		},

		//Bind View Presentation button
		bindButtonViewPresentation: function () {
			var self = this;
			$('.action-buttons').on('click', '#view-button', function(e) {
				e.preventDefault();
				window.open(WPPTaxonomyURL,'_blank');
			});
		},

		renumberColumns: function() {
			var i = 1;
			$( '.column' ).each(function( index ) {
				 var self = this;
				 var $self = $(this);
				 $self.find('> .widget-top > .widget-title > h4').html( i );
				i++;
			});
		},

		/**
		 * Close Modal
		 */
		closeModal: function() {
			tinymce.execCommand('mceRemoveControl',true,'editor_slide');
			$( '#dialog' ).dialog( "close" );
		}

	};

})(jQuery);
var slideManager = new WPPresentAdmin();<commit_msg>Change color of active column<commit_after>/**
 * WP Present - admin.js
 *
 */

var WPPresentAdmin;
(function($) {

	WPPresentAdmin = function() {
		this.init();
		return this;
	}

	WPPresentAdmin.prototype = {
		init: function() {
			var self = this;

			// Make the outer container resizeable
			$( "#outer-container" ).resizable();

			$( "#container" ).sortable({
				stop: function( event, ui ) {
					self.updateTaxonomyDescription();
				}
			});

			$( ".column-inner" ).sortable({
				connectWith: ".column-inner",
				stop: function( event, ui ) {
					self.updateTaxonomyDescription();
				}
			});

			self.widgetButtonExpand();
			self.widgetButtonEdit();
			self.widgetButtonDelete();
			self.widgetButtonAdd();
			self.widgetButtonTidy();
			self.bindButtonAddColumn();
			self.bindButtonViewPresentation();
			self.backfillColumns();
			self.refreshUI();

			// Select the first column on load
			self.activateColumn( $('#col-1').children('.widget-top').children('.widget-title') );

			$('.spinner').hide();

			return self;
		},

		/**
		 * Sortables and such
		 */
		refreshUI: function () {
			var self = this;

			$( "#container" ).sortable("refresh");

			// Append an inner column to each column that doesn't contain any slides.
			$('.column' ).not(":has(div.column-inner)").append('

');

			$( ".column-inner" ).sortable({
				connectWith: ".column-inner",
				stop: function( event, ui ) {
					self.updateTaxonomyDescription();
				}
			});

			// This really should be called only once inside init
			self.enableColumns();
		},

		/**
		 * Backfill Columns
		 */
		backfillColumns: function () {
			var self = this;
			var numSlides = WPPNumSlides[0];
			var numExisting = $('#container > .column').size();

			for (var col=numExisting+1;col<=numSlides;col++){
				self.addColumn();
			}
			//$('#container').append( '

' );
		},

		/**
		 * Make the column the target for adding new columns
		 */
		activateColumn: function ( $col ) {
			// Remove the active class from all columns
			$('.widget-title').css('background', '');
			$('.widget-title').css('color', '');
			$('.widget-title').removeClass('active');

			// Select the given column
			$col.css('background-color', '#0074a2').css('color','#ffffff');
			$col.addClass('active');
		},

		/**
		 * Encode,update, and save the columns data into the hidden taxonomy description field
		 */
		updateTaxonomyDescription: function () {
			var self = this;
			var columns = { }; // Creates a new object
			var i = 1;
			$( '.column-inner' ).each(function( index ) {
				var $order = $(this).sortable( "toArray" );
				columns['col-'+i] = $order;
				i++;
			});
			var encoded = JSON.stringify( columns );
			$('#description').val(encoded);

			// This was a convient time to do this UI clean-up
			self.renumberColumns();

			// Send this change off to ajax land
			self.updatePresentation();
		},

		/**
		 * AJAX request to update the current presentation taxonomy
		 */
		updatePresentation: function () {
			var nonce = $('#wp-present-nonce').val();
			var params = { 'content':$('#description').val(), 'id':presentation, 'nonce':nonce };
			$.ajax({
				url: ajaxurl + '?action=update_presentation',
				type: 'POST',
				data: jQuery.param(params),
				beforeSend: function( xhr ) {
					$('.spinner').show();
				},
				complete: function( xhr ) {
					//$('.spinner').hide();
				},
				success: function(result) {
					$('.spinner').hide();
					// Return the excerpt from the editor
					//console.log('presentation updated');
				}
			});
		},

		/**
		 * Bind Tidy button
		 */
		widgetButtonTidy: function () {
			var self = this;
			$('.action-buttons').on('click', '#tidy-button', function(e) {
				e.preventDefault();
				this.consolidateColumns();
				// Why does this break?
				//updatePresentation();
			});
		},

		/**
		 * Expand slide details
		 */
		widgetButtonExpand: function () {
			$('#container').on('click', '.widget-title-action', function(e) {
				$( this ).parents('.widget').children('.widget-inside').toggle();
			});
		},

		/**
		 * The action taken by the Tidy button
		 */
		consolidateColumns: function () {
			var self = this;
			var numCols = $('.column').length;
			// console.log(numCols);

			$('.column').each(function(outerIndex){

				// Don't have a col 0
				var outerIndex = outerIndex + 1;

				// Fixes the condition where we are looking at the last item
				if( outerIndex >= numCols )
					return;

				var $outerCol = $(this);
				var $innerCol = $outerCol.children('.column-inner');

				var innerHTML = $innerCol.html().trim();
				var outerHTML = $outerCol.html().trim();

				if( typeof(innerHTML) !== 'string' || '' == innerHTML ) {
					var $nextOuterCol = $('#col-'+(outerIndex+1));
					var $nextInnerCol = $nextOuterCol.children('.column-inner');

					var nextInnerHTML = $nextInnerCol.html().trim();
					var nextOuterHTML = $nextOuterCol.html().trim();

					$outerCol.html(nextOuterHTML);
					$nextOuterCol.html(outerHTML);
				}
			} );

			//Finally refresh description array
			$( ".column-inner" ).sortable({
				connectWith: ".column-inner",
				stop: (function( event, ui ) {
					self.updateTaxonomyDescription();
				}),
				create: (function( event, ui ) {
					self.updateTaxonomyDescription();
				})
			});
			self.updateTaxonomyDescription();
		},

		/**
		 * Setup the column to allow for activation
		 */
		enableColumns: function () {
			var self = this;
			$('.column').children('.widget-top').on('click', '.widget-title', function(e) {
				$col = $(this);
				self.activateColumn($col);
			});
		},

		// Bind Edit button
		widgetButtonEdit: function () {
			var self = this;
			$('#container').on('click', '.widget-control-edit', function(e) {
				e.preventDefault();
				var $button = $(this);
				var $parentWidget = $button.parents('.widget');
				var $widgetPreview = $parentWidget.find('.widget-preview');

				// Send the contents from the widget to the editor
				var contentEditor  = $widgetPreview.html();
				var widgetID = $parentWidget.find('.slide-id').val();
				var nonce = $('#wp-present-nonce').val();

				$('#editor_slide-tmce').click(); //Necessary on subsequent loads of the editor
				$( "#dialog" ).dialog({
				  autoOpen: true,
				  width: 640,
				  height: 640,
				  modal: false,
				  buttons: {
					"Update": function() {
						var editorContents = tinymce.get('editor_slide').getContent();
						var params = { 'content':editorContents, 'id':widgetID, 'nonce':nonce };
						// Send the contents of the existing post
						$.ajax({
							url: ajaxurl + '?action=update_slide',
							type: 'POST',
							data: jQuery.param(params),
							beforeSend: function( xhr ) {
								$('.spinner').show();
							},
							complete: function( xhr ) {
								//$('.spinner').hide();
							},
							success: function(result) {
								$('.spinner').hide();
								// Return the excerpt from the editor
								$widgetPreview.html( result );
						  }
						});
						self.closeModal();
					},
					Cancel: function() {
						self.closeModal();
					}
				  },
				  create: function() {
						tinymce.execCommand('mceRemoveControl',true,'editor_slide');
						tinymce.execCommand('mceAddControl',true,'editor_slide');
				  },
				  open: function() {
						// Load the contents of the existing post
						var nonce = $('#wp-present-nonce').val();
						var params = { 'id':widgetID, 'nonce':nonce };
						$.ajax({
							url: ajaxurl + '?action=get_slide',
							data: jQuery.param(params),
							beforeSend: function( xhr ) {
								$('.spinner').show();
							},
							complete: function( xhr ) {
								$('.spinner').hide();
							},
							success: function(contentEditor) {
								tinymce.get('editor_slide').setContent(contentEditor);
							}
						});

						// Hack for getting the reveal class added to tinymce editor body
						// @todo: look at wp_editor in wp/inc/class-wp-editor.php
						var $editorIframe = $('#editor_slide_ifr').contents();
						$editorIframe.find('body').addClass('reveal');
						$editorIframe.css('height','500px');

				  },
				  close: function() {
						self.closeModal();
				  }
				});
			});
		},

		// Bind Delete button
		widgetButtonDelete: function () {
			var self = this;
			$('#container').on('click', '.widget-control-remove', function(e) {
				e.preventDefault();

				var confirmDelete = confirm("You are about to permanently delete the selected slide. 'Cancel' to stop, 'OK' to delete.");
				if( false == confirmDelete )
					return;

				var $button = $(this);
				var $parentWidget = $button.parents('.widget');
				var widgetID = $parentWidget.find('.slide-id').val();
				var nonce = $('#wp-present-nonce').val();
				var params = { 'id':widgetID, 'nonce':nonce };

				$.ajax({
					url: ajaxurl + '?action=delete_slide',
					type: 'POST',
					data: jQuery.param(params),
					beforeSend: function( xhr ) {
						$('.spinner').show();
					},
					complete: function( xhr ) {
						//$('.spinner').hide();
					},
					success: function(result) {
						$('.spinner').hide();
						$parentWidget.remove();
						self.updateTaxonomyDescription();
					}
				});
			});
		},

		// Bind Add button
		widgetButtonAdd: function () {
			var self = this;
			$('.action-buttons').on('click', '#add-button', function(e) {
				e.preventDefault();
				var $button = $(this);
				var $activeColumn = $('.widget-title.active').parent('.widget-top').parent('.column').children('.column-inner');

				$('#editor_slide-tmce').click(); //Necessary on subsequent loads of the editor
				$( "#dialog" ).dialog({
				  autoOpen: true,
				  width: 600,
				  height: 600,
				  modal: false,
				  buttons: {
					"Publish": function() {
						var editorContents = tinymce.get('editor_slide').getContent();
						var nonce = $('#wp-present-nonce').val();
						var params = { content:editorContents,'presentation':presentation, 'nonce':nonce };
						// Send the contents of the existing post
						$.ajax({
							url: ajaxurl + '?action=new_slide',
							type: 'POST',
							data: jQuery.param(params),
							success: function(result) {
								$activeColumn.append(result);
								WPPNumSlides[0]++;
								self.addColumn();
								self.refreshUI();
								self.updateTaxonomyDescription();
						  }
						});
						self.closeModal();
					},
					Cancel: function() {
						self.closeModal();
					}
				  },
				  create: function() {
						tinymce.execCommand('mceRemoveControl',true,'editor_slide');
						tinymce.execCommand('mceAddControl',true,'editor_slide');
				  },
				  open: function() {
						// Clear the editor
						tinymce.get('editor_slide').setContent('');
						// Hack for getting the reveal class added to tinymce editor body
						var $editorIframe = $('#editor_slide_ifr').contents();
						$editorIframe.find('body').addClass('reveal');
				  },
				  close: function() {
						self.closeModal();
				  }
				});
			});
		},

		// Adds a column to the presentation
		addColumn: function () {
			var self = this;
			var col = $('#container > .column').size() + 1;

			$('#container').append( '

'+col+'

' );
			$('#container').width( $('#container').width() + 210 );
		},

		//Bind Add Column button to addColumn()
		bindButtonAddColumn: function () {
			var self = this;
			$('.action-buttons').on('click', '#add-column', function(e) {
				e.preventDefault();
				self.addColumn();
				self.refreshUI();
			});
		},

		//Bind View Presentation button
		bindButtonViewPresentation: function () {
			var self = this;
			$('.action-buttons').on('click', '#view-button', function(e) {
				e.preventDefault();
				window.open(WPPTaxonomyURL,'_blank');
			});
		},

		renumberColumns: function() {
			var i = 1;
			$( '.column' ).each(function( index ) {
				 var self = this;
				 var $self = $(this);
				 $self.find('> .widget-top > .widget-title > h4').html( i );
				i++;
			});
		},

		/**
		 * Close Modal
		 */
		closeModal: function() {
			tinymce.execCommand('mceRemoveControl',true,'editor_slide');
			$( '#dialog' ).dialog( "close" );
		}

	};

})(jQuery);
var slideManager = new WPPresentAdmin();<|endoftext|>


<commit_before>/* ========================================================================
 * Bootstrap: affix.js v3.2.0
 * http://getbootstrap.com/javascript/#affix
 * ========================================================================
 * Copyright 2011-2014 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

+function ($) {
  'use strict';

  // AFFIX CLASS DEFINITION
  // ======================

  var Affix = function (element, options) {
    this.options = $.extend({}, Affix.DEFAULTS, options)

    this.$target = $(this.options.target)
      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))

    this.$element     = $(element)
    this.affixed      =
    this.unpin        =
    this.pinnedOffset = null

    this.checkPosition()
  }

  Affix.VERSION  = '3.2.0'

  Affix.RESET    = 'affix affix-top affix-bottom'

  Affix.DEFAULTS = {
    offset: 0,
    target: window
  }

  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
    var scrollTop     = this.$target.scrollTop()
    var position      = this.$element.offset()
    var targetHeight  = this.$target.height()

    if (offsetTop != null && this.affixed == 'top') return scrollTop >= offsetTop ? false : 'top'
    if (this.affixed == 'bottom') {
      // Can be affixed to the top, use the unpin value
      if (offsetTop != null) {
        return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
      // Can only ever be pinned or affixed to the bottom, ignore unpin value
      } else {
        return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
      }
    } else {
      var initializing   = this.affixed == null
      var colliderTop    = initializing ? scrollTop : position.top
      var colliderHeight = initializing ? targetHeight : height

      if (offsetTop != null && colliderTop <= offsetTop) {
        return 'top'
      } else if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) {
        return 'bottom'
      } else {
        return false
      }
    }
  }

  Affix.prototype.getPinnedOffset = function () {
    if (this.pinnedOffset) return this.pinnedOffset
    this.$element.removeClass(Affix.RESET).addClass('affix')
    var scrollTop = this.$target.scrollTop()
    var position  = this.$element.offset()
    return (this.pinnedOffset = position.top - scrollTop)
  }

  Affix.prototype.checkPositionWithEventLoop = function () {
    setTimeout($.proxy(this.checkPosition, this), 1)
  }

  Affix.prototype.checkPosition = function () {
    if (!this.$element.is(':visible')) return

    var scrollHeight = $('body').height()
    var height       = this.$element.height()
    var offset       = this.options.offset
    var offsetTop    = offset.top
    var offsetBottom = offset.bottom

    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)

    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)

    if (this.affixed != affix) {
      if (this.unpin != null) this.$element.css('top', '')

      var affixType = 'affix' + (affix ? '-' + affix : '')
      var e         = $.Event(affixType + '.bs.affix')

      this.$element.trigger(e)

      if (e.isDefaultPrevented()) return

      this.affixed = affix
      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null

      this.$element
        .removeClass(Affix.RESET)
        .addClass(affixType)
        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
    }

    if (affix == 'bottom') {
      this.$element.offset({
        top: scrollHeight - height - offsetBottom
      })
    }
  }

  // AFFIX PLUGIN DEFINITION
  // =======================

  function Plugin(option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.affix')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  var old = $.fn.affix

  $.fn.affix             = Plugin
  $.fn.affix.Constructor = Affix

  // AFFIX NO CONFLICT
  // =================

  $.fn.affix.noConflict = function () {
    $.fn.affix = old
    return this
  }

  // AFFIX DATA-API
  // ==============

  $(window).on('load', function () {
    $('[data-spy="affix"]').each(function () {
      var $spy = $(this)
      var data = $spy.data()

      data.offset = data.offset || {}

      if (data.offsetBottom) data.offset.bottom = data.offsetBottom
      if (data.offsetTop)    data.offset.top    = data.offsetTop

      Plugin.call($spy, data)
    })
  })

}(jQuery);
<commit_msg>cleanup style<commit_after>/* ========================================================================
 * Bootstrap: affix.js v3.2.0
 * http://getbootstrap.com/javascript/#affix
 * ========================================================================
 * Copyright 2011-2014 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== */

+function ($) {
  'use strict';

  // AFFIX CLASS DEFINITION
  // ======================

  var Affix = function (element, options) {
    this.options = $.extend({}, Affix.DEFAULTS, options)

    this.$target = $(this.options.target)
      .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
      .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))

    this.$element     = $(element)
    this.affixed      =
    this.unpin        =
    this.pinnedOffset = null

    this.checkPosition()
  }

  Affix.VERSION  = '3.2.0'

  Affix.RESET    = 'affix affix-top affix-bottom'

  Affix.DEFAULTS = {
    offset: 0,
    target: window
  }

  Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) {
    var scrollTop    = this.$target.scrollTop()
    var position     = this.$element.offset()
    var targetHeight = this.$target.height()

    if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false

    if (this.affixed == 'bottom') {
      if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom'
      return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom'
    }

    var initializing   = this.affixed == null
    var colliderTop    = initializing ? scrollTop : position.top
    var colliderHeight = initializing ? targetHeight : height

    if (offsetTop != null && colliderTop <= offsetTop) return 'top'
    if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom'

    return false
  }

  Affix.prototype.getPinnedOffset = function () {
    if (this.pinnedOffset) return this.pinnedOffset
    this.$element.removeClass(Affix.RESET).addClass('affix')
    var scrollTop = this.$target.scrollTop()
    var position  = this.$element.offset()
    return (this.pinnedOffset = position.top - scrollTop)
  }

  Affix.prototype.checkPositionWithEventLoop = function () {
    setTimeout($.proxy(this.checkPosition, this), 1)
  }

  Affix.prototype.checkPosition = function () {
    if (!this.$element.is(':visible')) return

    var height       = this.$element.height()
    var offset       = this.options.offset
    var offsetTop    = offset.top
    var offsetBottom = offset.bottom
    var scrollHeight = $('body').height()

    if (typeof offset != 'object')         offsetBottom = offsetTop = offset
    if (typeof offsetTop == 'function')    offsetTop    = offset.top(this.$element)
    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)

    var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom)

    if (this.affixed != affix) {
      if (this.unpin != null) this.$element.css('top', '')

      var affixType = 'affix' + (affix ? '-' + affix : '')
      var e         = $.Event(affixType + '.bs.affix')

      this.$element.trigger(e)

      if (e.isDefaultPrevented()) return

      this.affixed = affix
      this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null

      this.$element
        .removeClass(Affix.RESET)
        .addClass(affixType)
        .trigger(affixType.replace('affix', 'affixed') + '.bs.affix')
    }

    if (affix == 'bottom') {
      this.$element.offset({
        top: scrollHeight - height - offsetBottom
      })
    }
  }

  // AFFIX PLUGIN DEFINITION
  // =======================

  function Plugin(option) {
    return this.each(function () {
      var $this   = $(this)
      var data    = $this.data('bs.affix')
      var options = typeof option == 'object' && option

      if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
      if (typeof option == 'string') data[option]()
    })
  }

  var old = $.fn.affix

  $.fn.affix             = Plugin
  $.fn.affix.Constructor = Affix

  // AFFIX NO CONFLICT
  // =================

  $.fn.affix.noConflict = function () {
    $.fn.affix = old
    return this
  }

  // AFFIX DATA-API
  // ==============

  $(window).on('load', function () {
    $('[data-spy="affix"]').each(function () {
      var $spy = $(this)
      var data = $spy.data()

      data.offset = data.offset || {}

      if (data.offsetBottom) data.offset.bottom = data.offsetBottom
      if (data.offsetTop)    data.offset.top    = data.offsetTop

      Plugin.call($spy, data)
    })
  })

}(jQuery);
<|endoftext|>


<commit_before>var streams = [
    ['audio/ogg', "http://engine.wuvt.vt.edu:8000/wuvt.ogg"],
    ['audio/aac', "http://engine.wuvt.vt.edu:8000/wuvt.aac"]
];
var streamPlaying = false;

function initPlayer() {
    var audioTag = document.createElement('audio');

    for(i in streams) {
        var stream = streams[i];
        if(audioTag.canPlayType(stream[0])) {
            initStream(stream[1]);
            return;
        }
    }

    $('#stream_btn').addClass('disabled');
    $('#stream_btn').click(warnBrokenPlayer);

    $('#volume_btn').addClass('disabled');
    $('#volume_btn').click(warnBrokenPlayer);
}

function initStream(streamUrl) {
    // This doesn't work on Chrome for Android:
    // https://code.google.com/p/chromium/issues/detail?id=178297

    $('#stream_btn').click(function() {
        if(!streamPlaying) {
            var stream = document.createElement('audio');
            $(stream).attr('id', "wuvt_stream");
            $(stream).attr('src', streamUrl);
            $(stream).attr('autoplay', "autoplay");
            $('body').append(stream);

            stream.addEventListener('play', function() {
                $('#stream_btn').addClass('playing');
                $('#stream_btn').attr('title', "Stop");
                $('#stream_btn').removeAttr('disabled');
            });

            // workaround to deal with playback stopping when metadata changes
            // https://bugzil.la/455165
            stream.addEventListener("ended", function() {
                $('#wuvt_stream').attr('src', "");
                $('#wuvt_stream').attr('src', streamUrl);
            });

            // chrome workaround for the same thing
            // https://code.google.com/p/chromium/issues/detail?id=175281
            stream.addEventListener("error", function() {
                $('#wuvt_stream').attr('src', "");
                $('#wuvt_stream').attr('src', streamUrl);
                $('#wuvt_stream').trigger('play');
            });

            streamPlaying = true;
            $('#stream_btn').attr('title', "Buffering...");
            $('#stream_btn').attr('disabled', "disabled");
        }
        else {
            $('#wuvt_stream').attr('src', "");
            $('#wuvt_stream').remove();

            streamPlaying = false;
            $('#stream_btn').removeClass('playing');
            $('#stream_btn').attr('title', "Play");

            $('#volume_box').removeClass('visible');
        }

        return false;
    });

    initVolume();
}

function initVolume() {
    var volbox = document.createElement('div');
    $(volbox).attr('id', "volume_box");

    var slider = document.createElement('div');
    $(slider).attr('id', "volume_slider");
    $(volbox).append(slider);

    var mutebtn = document.createElement('button');
    $(mutebtn).attr('id', "volume_mute_btn");
    $(mutebtn).attr('title', "Mute");
    $(mutebtn).text("Mute");
    $(volbox).append(mutebtn);

    $('#mainheader').append(volbox);

    $('#volume_btn').click(function() {
        var btn = $('#volume_btn');
        var offsetTop = btn.offset().top + btn.outerHeight();
        var offsetLeft = btn.offset().left + (btn.outerWidth() / 2) -
            ($('#volume_box').outerWidth() / 2);

        $('#volume_box').css('top', offsetTop + "px");
        $('#volume_box').css('left', offsetLeft + "px");
        $('#volume_box').toggleClass('visible');
    });

    $('#volume_slider').slider({
        'orientation': "vertical",
        'value': 1,
        'step': 0.05,
        'range': "min",
        'max': 1,
        'animate': false,
        'slide': function(e, slider) {
            $('#wuvt_stream').prop('muted', false);
            $('#wuvt_stream').prop('volume', slider.value);
        },
    });

    $('#volume_mute_btn').click(function() {
        if(!$('#wuvt_stream').prop('muted')) {
            $('#wuvt_stream').prop('muted', true);
            $('#volume_slider').slider('option', 'value', 0);
        }
        else {
            $('#wuvt_stream').prop('muted', false);
            $('#volume_slider').slider('option', 'value',
                $('#wuvt_stream').prop('volume'));
        }
    });
}

function warnBrokenPlayer() {
    alert("Sorry, the WUVT live stream player is not supported by your browser. Please see the Listen Live page for other options, or switch to Google Chrome or Mozilla Firefox.");
}
<commit_msg>stream: add alert if stream fails to start<commit_after>var streams = [
    ['audio/ogg', "http://engine.wuvt.vt.edu:8000/wuvt.ogg"],
    ['audio/aac', "http://engine.wuvt.vt.edu:8000/wuvt.aac"]
];
var streamPlaying = false;

function initPlayer() {
    var audioTag = document.createElement('audio');

    for(i in streams) {
        var stream = streams[i];
        if(audioTag.canPlayType(stream[0])) {
            initStream(stream[1]);
            return;
        }
    }

    $('#stream_btn').addClass('disabled');
    $('#stream_btn').click(warnBrokenPlayer);

    $('#volume_btn').addClass('disabled');
    $('#volume_btn').click(warnBrokenPlayer);
}

function initStream(streamUrl) {
    // This doesn't work on Chrome for Android:
    // https://code.google.com/p/chromium/issues/detail?id=178297

    $('#stream_btn').click(function() {
        if(!streamPlaying) {
            var stream = document.createElement('audio');
            $(stream).attr('id', "wuvt_stream");
            $(stream).attr('src', streamUrl);
            $(stream).attr('autoplay', "autoplay");
            $('body').append(stream);

            stream.addEventListener('play', function() {
                $('#stream_btn').addClass('playing');
                $('#stream_btn').attr('title', "Stop");
                $('#stream_btn').removeAttr('disabled');
            });

            // workaround to deal with playback stopping when metadata changes
            // https://bugzil.la/455165
            stream.addEventListener("ended", function() {
                $('#wuvt_stream').attr('src', "");
                $('#wuvt_stream').attr('src', streamUrl);
            });

            // chrome workaround for the same thing
            // https://code.google.com/p/chromium/issues/detail?id=175281
            stream.addEventListener("error", function() {
                $('#wuvt_stream').attr('src', "");
                $('#wuvt_stream').attr('src', streamUrl);
                $('#wuvt_stream').trigger('play');
            });

            // display an alert after 5 seconds if the stream hasn't started
            window.setTimeout(function() {
                if(streamPlaying && !$('#stream_btn').hasClass('playing')) {
                    alert("Streaming problems? Check the Listen Live page for other ways to listen to WUVT!");
                }
            }, 5000);

            streamPlaying = true;
            $('#stream_btn').attr('title', "Buffering...");
            $('#stream_btn').attr('disabled', "disabled");
        }
        else {
            $('#wuvt_stream').attr('src', "");
            $('#wuvt_stream').remove();

            streamPlaying = false;
            $('#stream_btn').removeClass('playing');
            $('#stream_btn').attr('title', "Play");

            $('#volume_box').removeClass('visible');
        }

        return false;
    });

    initVolume();
}

function initVolume() {
    var volbox = document.createElement('div');
    $(volbox).attr('id', "volume_box");

    var slider = document.createElement('div');
    $(slider).attr('id', "volume_slider");
    $(volbox).append(slider);

    var mutebtn = document.createElement('button');
    $(mutebtn).attr('id', "volume_mute_btn");
    $(mutebtn).attr('title', "Mute");
    $(mutebtn).text("Mute");
    $(volbox).append(mutebtn);

    $('#mainheader').append(volbox);

    $('#volume_btn').click(function() {
        var btn = $('#volume_btn');
        var offsetTop = btn.offset().top + btn.outerHeight();
        var offsetLeft = btn.offset().left + (btn.outerWidth() / 2) -
            ($('#volume_box').outerWidth() / 2);

        $('#volume_box').css('top', offsetTop + "px");
        $('#volume_box').css('left', offsetLeft + "px");
        $('#volume_box').toggleClass('visible');
    });

    $('#volume_slider').slider({
        'orientation': "vertical",
        'value': 1,
        'step': 0.05,
        'range': "min",
        'max': 1,
        'animate': false,
        'slide': function(e, slider) {
            $('#wuvt_stream').prop('muted', false);
            $('#wuvt_stream').prop('volume', slider.value);
        },
    });

    $('#volume_mute_btn').click(function() {
        if(!$('#wuvt_stream').prop('muted')) {
            $('#wuvt_stream').prop('muted', true);
            $('#volume_slider').slider('option', 'value', 0);
        }
        else {
            $('#wuvt_stream').prop('muted', false);
            $('#volume_slider').slider('option', 'value',
                $('#wuvt_stream').prop('volume'));
        }
    });
}

function warnBrokenPlayer() {
    alert("Sorry, the WUVT live stream player is not supported by your browser. Please see the Listen Live page for other ways to listen to WUVT, or switch to Google Chrome or Mozilla Firefox.");
}
<|endoftext|>


<commit_before>var exec = require('cordova/exec');

var baidumap_location = {
    getCurrentPosition: function (successCallback, errorCallback, options) {

        // Timer var that will fire an error callback if no position is retrieved from native
        // before the "timeout" param provided expires
        var timeoutTimer = {timer: null};

        var win = function (p) {
            clearTimeout(timeoutTimer.timer);
            if (!(timeoutTimer.timer)) {
                // Timeout already happened, or native fired error callback for
                // this geo request.
                // Don't continue with success callback.
                return;
            }
            successCallback(p);
        };
        var fail = function (e) {
            clearTimeout(timeoutTimer.timer);
            timeoutTimer.timer = null;
            if (errorCallback) {
                errorCallback(e);
            }
        };

        if (options && options.timeout !== Infinity) {
            // If the timeout value was not set to Infinity (default), then
            // set up a timeout function that will fire the error callback
            // if no successful position was retrieved before timeout expired.
            timeoutTimer.timer = createTimeout(fail, options.timeout);
        } else {
            // This is here so the check in the win function doesn't mess stuff up
            // may seem weird but this guarantees timeoutTimer is
            // always truthy before we call into native
            timeoutTimer.timer = true;
        }
        exec(win, fail, 'BaiduMapLocation', 'getCurrentPosition', options);
        return timeoutTimer
    }
};

// Returns a timeout failure, closed over a specified timeout value and error callback.
function createTimeout(errorCallback, timeout) {
    var t = setTimeout(function () {
        clearTimeout(t);
        t = null;
        errorCallback({
            code: -1,
            message: "Position retrieval timed out."
        });
    }, timeout);
    return t;
}

module.exports = baidumap_location<commit_msg>Update baidumap_location.js<commit_after>var exec = require('cordova/exec');

var baidumap_location = {
    getCurrentPosition: function (successCallback, errorCallback, options) {

        // Timer var that will fire an error callback if no position is retrieved from native
        // before the "timeout" param provided expires
        var timeoutTimer = {timer: null};

        var win = function (p) {
            clearTimeout(timeoutTimer.timer);
            if (!(timeoutTimer.timer)) {
                // Timeout already happened, or native fired error callback for
                // this geo request.
                // Don't continue with success callback.
                return;
            }
            successCallback(p);
        };
        var fail = function (e) {
            clearTimeout(timeoutTimer.timer);
            timeoutTimer.timer = null;
            if (errorCallback) {
                errorCallback(e);
            }
        };

        if (options && options.timeout !== Infinity) {
            // If the timeout value was not set to Infinity (default), then
            // set up a timeout function that will fire the error callback
            // if no successful position was retrieved before timeout expired.
            timeoutTimer.timer = createTimeout(fail, options.timeout);
        } else {
            // This is here so the check in the win function doesn't mess stuff up
            // may seem weird but this guarantees timeoutTimer is
            // always truthy before we call into native
            timeoutTimer.timer = true;
        }
        exec(win, fail, 'BaiduMapLocation', 'getCurrentPosition', [options]);
        return timeoutTimer
    }
};

// Returns a timeout failure, closed over a specified timeout value and error callback.
function createTimeout(errorCallback, timeout) {
    var t = setTimeout(function () {
        clearTimeout(t);
        t = null;
        errorCallback({
            code: -1,
            message: "Position retrieval timed out."
        });
    }, timeout);
    return t;
}

module.exports = baidumap_location
<|endoftext|>


<commit_before>var gulp = require('gulp');
var concat = require('gulp-concat');
var connect = require('gulp-connect');
var eslint = require('gulp-eslint');
var file = require('gulp-file');
var htmlv = require('gulp-html-validator');
var insert = require('gulp-insert');
var replace = require('gulp-replace');
var size = require('gulp-size');
var streamify = require('gulp-streamify');
var uglify = require('gulp-uglify');
var util = require('gulp-util');
var zip = require('gulp-zip');
var exec = require('child-process-promise').exec;
var karma = require('karma');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var merge = require('merge-stream');
var collapse = require('bundle-collapser/plugin');
var argv  = require('yargs').argv
var path = require('path');
var package = require('./package.json');

var srcDir = './src/';
var outDir = './dist/';
var testDir = './test/';

var header = "/*!\n" +
  " * Chart.js\n" +
  " * http://chartjs.org/\n" +
  " * Version: {{ version }}\n" +
  " *\n" +
  " * Copyright 2017 Nick Downie\n" +
  " * Released under the MIT license\n" +
  " * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md\n" +
  " */\n";

gulp.task('bower', bowerTask);
gulp.task('build', buildTask);
gulp.task('package', packageTask);
gulp.task('watch', watchTask);
gulp.task('lint', lintTask);
gulp.task('docs', docsTask);
gulp.task('test', ['lint', 'validHTML', 'unittest']);
gulp.task('size', ['library-size', 'module-sizes']);
gulp.task('server', serverTask);
gulp.task('validHTML', validHTMLTask);
gulp.task('unittest', unittestTask);
gulp.task('library-size', librarySizeTask);
gulp.task('module-sizes', moduleSizesTask);
gulp.task('_open', _openTask);
gulp.task('dev', ['server', 'default']);
gulp.task('default', ['build', 'watch']);

/**
 * Generates the bower.json manifest file which will be pushed along release tags.
 * Specs: https://github.com/bower/spec/blob/master/json.md
 */
function bowerTask() {
  var json = JSON.stringify({
      name: package.name,
      description: package.description,
      homepage: package.homepage,
      license: package.license,
      version: package.version,
      main: outDir + "Chart.js",
      ignore: [
        '.github',
        '.codeclimate.yml',
        '.gitignore',
        '.npmignore',
        '.travis.yml',
        'scripts'
      ]
    }, null, 2);

  return file('bower.json', json, { src: true })
    .pipe(gulp.dest('./'));
}

function buildTask() {

  var bundled = browserify('./src/chart.js', { standalone: 'Chart' })
    .plugin(collapse)
    .bundle()
    .pipe(source('Chart.bundle.js'))
    .pipe(insert.prepend(header))
    .pipe(streamify(replace('{{ version }}', package.version)))
    .pipe(gulp.dest(outDir))
    .pipe(streamify(uglify()))
    .pipe(insert.prepend(header))
    .pipe(streamify(replace('{{ version }}', package.version)))
    .pipe(streamify(concat('Chart.bundle.min.js')))
    .pipe(gulp.dest(outDir));

  var nonBundled = browserify('./src/chart.js', { standalone: 'Chart' })
    .ignore('moment')
    .plugin(collapse)
    .bundle()
    .pipe(source('Chart.js'))
    .pipe(insert.prepend(header))
    .pipe(streamify(replace('{{ version }}', package.version)))
    .pipe(gulp.dest(outDir))
    .pipe(streamify(uglify()))
    .pipe(insert.prepend(header))
    .pipe(streamify(replace('{{ version }}', package.version)))
    .pipe(streamify(concat('Chart.min.js')))
    .pipe(gulp.dest(outDir));

  return merge(bundled, nonBundled);

}

function packageTask() {
  return merge(
      // gather "regular" files landing in the package root
      gulp.src([outDir + '*.js', 'LICENSE.md']),

      // since we moved the dist files one folder up (package root), we need to rewrite
      // samples src="../dist/ to src="../ and then copy them in the /samples directory.
      gulp.src('./samples/**/*', { base: '.' })
        .pipe(streamify(replace(/src="((?:\.\.\/)+)dist\//g, 'src="$1')))
  )
  // finally, create the zip archive
  .pipe(zip('Chart.js.zip'))
  .pipe(gulp.dest(outDir));
}

function lintTask() {
  var files = [
    srcDir + '**/*.js',
    testDir + '**/*.js'
  ];

  // NOTE(SB) codeclimate has 'complexity' and 'max-statements' eslint rules way too strict
  // compare to what the current codebase can support, and since it's not straightforward
  // to fix, let's turn them as warnings and rewrite code later progressively.
  var options = {
    rules: {
      'complexity': [1, 6],
      'max-statements': [1, 30]
    },
    globals: [
      'Chart',
      'acquireChart',
      'afterAll',
      'afterEach',
      'beforeAll',
      'beforeEach',
      'describe',
      'expect',
      'fail',
      'it',
      'jasmine',
      'moment',
      'spyOn',
      'xit'
    ]
  };

  return gulp.src(files)
    .pipe(eslint(options))
    .pipe(eslint.format())
    .pipe(eslint.failAfterError());
}

function docsTask(done) {
  const script = require.resolve('gitbook-cli/bin/gitbook.js');
  const cmd = process.execPath;

  exec([cmd, script, 'install', './'].join(' ')).then(() => {
    return exec([cmd, script, 'build', './', './dist/docs'].join(' '));
  }).catch((err) => {
    console.error(err.stdout);
  }).then(() => {
    done();
  });
}

function validHTMLTask() {
  return gulp.src('samples/*.html')
    .pipe(htmlv());
}

function startTest() {
  return [
    {pattern: './test/fixtures/**/*.json', included: false},
    {pattern: './test/fixtures/**/*.png', included: false},
    './node_modules/moment/min/moment.min.js',
    './test/jasmine.index.js',
    './src/**/*.js',
  ].concat(
    argv.inputs?
      argv.inputs.split(';'):
      ['./test/specs/**/*.js']
  );
}

function unittestTask(done) {
  new karma.Server({
    configFile: path.join(__dirname, 'karma.conf.js'),
    singleRun: !argv.watch,
    files: startTest(),
    args: {
      coverage: !!argv.coverage
    }
  }, done).start();
}

function librarySizeTask() {
  return gulp.src('dist/Chart.bundle.min.js')
    .pipe(size({
      gzip: true
    }));
}

function moduleSizesTask() {
  return gulp.src(srcDir + '**/*.js')
    .pipe(uglify({
      preserveComments: 'some'
    }))
    .pipe(size({
      showFiles: true,
      gzip: true
    }));
}

function watchTask() {
  if (util.env.test) {
    return gulp.watch('./src/**', ['build', 'unittest', 'unittestWatch']);
  }
  return gulp.watch('./src/**', ['build']);
}

function serverTask() {
  connect.server({
    port: 8000
  });
}

// Convenience task for opening the project straight from the command line

function _openTask() {
  exec('open http://localhost:8000');
  exec('subl .');
}
<commit_msg>Delete gulpfile.js<commit_after><|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @author       Felipe Alfonso <@bitnenfer>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../../utils/Class');
var ShaderSourceFS = require('../shaders/ForwardDiffuse-frag.js');
var TextureTintPipeline = require('./TextureTintPipeline');
var WebGLPipeline = require('../WebGLPipeline');

var LIGHT_COUNT = 10;

/**
 * @classdesc
 * ForwardDiffuseLightPipeline implements a forward rendering approach for 2D lights.
 * This pipeline extends TextureTintPipeline so it implements all it's rendering functions
 * and batching system.
 *
 * @class ForwardDiffuseLightPipeline
 * @extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline
 * @memberof Phaser.Renderer.WebGL.Pipelines
 * @constructor
 * @since 3.0.0
 *
 * @param {object} config - The configuration of the pipeline, same as the {@link Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline}. The fragment shader will be replaced with the lighting shader.
 */
var ForwardDiffuseLightPipeline = new Class({

    Extends: TextureTintPipeline,

    initialize:

    function ForwardDiffuseLightPipeline (config)
    {
        LIGHT_COUNT = config.maxLights;

        config.fragShader = ShaderSourceFS.replace('%LIGHT_COUNT%', LIGHT_COUNT.toString());

        TextureTintPipeline.call(this, config);

        /**
         * Inverse rotation matrix for normal map rotations.
         *
         * @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#inverseRotationMatrix
         * @type {Float32Array}
         * @private
         * @since 3.16.0
         */
        this.inverseRotationMatrix = new Float32Array([
            1, 0, 0,
            0, 1, 0,
            0, 0, 1
        ]);

        /**
         * Stores a default normal map, which is an object with a `glTexture` property that
         * maps to a 1x1 texture of the color #7f7fff created in the `boot` method.
         *
         * @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#defaultNormalMap
         * @type {object}
         * @since 3.25.0
         */
        this.defaultNormalMap;

        /**
         * Stores the previous number of lights rendered.
         *
         * @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#lightCount
         * @type {number}
         * @since 3.25.0
         */
        this.lightCount = 0;
    },

    /**
     * Called when the Game has fully booted and the Renderer has finished setting up.
     *
     * By this stage all Game level systems are now in place and you can perform any final
     * tasks that the pipeline may need that relied on game systems such as the Texture Manager.
     *
     * @method Phaser.Renderer.WebGL.ForwardDiffuseLightPipeline#boot
     * @since 3.11.0
     */
    boot: function ()
    {
        WebGLPipeline.prototype.boot.call(this);

        var gl = this.gl;

        var tempTexture = gl.createTexture();

        gl.activeTexture(gl.TEXTURE0);

        gl.bindTexture(gl.TEXTURE_2D, tempTexture);

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([ 127, 127, 255, 255 ]));

        this.defaultNormalMap = { glTexture: tempTexture };

        return this;
    },

    /**
     * Called every time the pipeline is bound by the renderer.
     * Sets the shader program, vertex buffer and other resources.
     * Should only be called when changing pipeline.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#bind
     * @since 3.25.0
     *
     * @return {this} This WebGLPipeline instance.
     */
    bind: function ()
    {
        WebGLPipeline.prototype.bind.call(this);

        var renderer = this.renderer;
        var program = this.program;

        renderer.setInt1(program, 'uMainSampler', 0);
        renderer.setInt1(program, 'uNormSampler', 1);
        renderer.setFloat2(program, 'uResolution', this.width, this.height);

        return this;
    },

    /**
     * This function sets all the needed resources for each camera pass.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#onRender
     * @since 3.0.0
     *
     * @param {Phaser.Scene} scene - The Scene being rendered.
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Scene Camera being rendered with.
     *
     * @return {this} This WebGLPipeline instance.
     */
    onRender: function (scene, camera)
    {
        this.active = false;

        var lightManager = scene.sys.lights;

        if (!lightManager || lightManager.lights.length <= 0 || !lightManager.active)
        {
            //  Passthru
            return this;
        }

        var lights = lightManager.cull(camera);
        var lightCount = Math.min(lights.length, LIGHT_COUNT);

        if (lightCount === 0)
        {
            return this;
        }

        this.active = true;

        var renderer = this.renderer;
        var program = this.program;
        var cameraMatrix = camera.matrix;
        var point = {x: 0, y: 0};
        var height = renderer.height;
        var i;

        if (lightCount !== this.lightCount)
        {
            for (i = 0; i < LIGHT_COUNT; i++)
            {
                //  Reset lights
                renderer.setFloat1(program, 'uLights[' + i + '].radius', 0);
            }

            this.lightCount = lightCount;
        }

        if (camera.dirty)
        {
            renderer.setFloat4(program, 'uCamera', camera.x, camera.y, camera.rotation, camera.zoom);
        }

        //  TODO - Only if dirty! and cache the location
        renderer.setFloat3(program, 'uAmbientLightColor', lightManager.ambientColor.r, lightManager.ambientColor.g, lightManager.ambientColor.b);

        for (i = 0; i < lightCount; i++)
        {
            var light = lights[i];
            var lightName = 'uLights[' + i + '].';

            cameraMatrix.transformPoint(light.x, light.y, point);

            //  TODO - Cache the uniform locations!!!
            renderer.setFloat2(program, lightName + 'position', point.x - (camera.scrollX * light.scrollFactorX * camera.zoom), height - (point.y - (camera.scrollY * light.scrollFactorY) * camera.zoom));

            if (light.dirty)
            {
                renderer.setFloat3(program, lightName + 'color', light.r, light.g, light.b);
                renderer.setFloat1(program, lightName + 'intensity', light.intensity);
                renderer.setFloat1(program, lightName + 'radius', light.radius);
                light.dirty = false;
            }
        }

        this.currentNormalMapRotation = null;

        return this;
    },

    /**
     * Rotates the normal map vectors inversely by the given angle.
     * Only works in 2D space.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setNormalMapRotation
     * @since 3.16.0
     *
     * @param {number} rotation - The angle of rotation in radians.
     */
    setNormalMapRotation: function (rotation)
    {
        if (rotation !== this.currentNormalMapRotation || this.vertexCount === 0)
        {
            if (this.vertexCount > 0)
            {
                this.flush();
            }

            var inverseRotationMatrix = this.inverseRotationMatrix;

            if (rotation)
            {
                var rot = -rotation;
                var c = Math.cos(rot);
                var s = Math.sin(rot);

                inverseRotationMatrix[1] = s;
                inverseRotationMatrix[3] = -s;
                inverseRotationMatrix[0] = inverseRotationMatrix[4] = c;
            }
            else
            {
                inverseRotationMatrix[0] = inverseRotationMatrix[4] = 1;
                inverseRotationMatrix[1] = inverseRotationMatrix[3] = 0;
            }

            this.renderer.setMatrix3(this.program, 'uInverseRotationMatrix', false, inverseRotationMatrix);

            this.currentNormalMapRotation = rotation;
        }
    },

    /**
     * Assigns a texture to the current batch. If a different texture is already set it creates a new batch object.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setTexture2D
     * @since 3.25.0
     *
     * @param {WebGLTexture} [texture] - WebGLTexture that will be assigned to the current batch. If not given uses blankTexture.
     * @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object being rendered or added to the batch.
     */
    setTexture2D: function (texture, gameObject)
    {
        var renderer = this.renderer;

        if (texture === undefined) { texture = renderer.tempTextures[0]; }

        var normalTexture = this.getNormalMap(gameObject);

        if (renderer.isNewNormalMap())
        {
            this.flush();

            renderer.setTextureZero(texture);
            renderer.setNormalMap(normalTexture);
        }

        if (gameObject)
        {
            this.setNormalMapRotation(gameObject.rotation);
        }

        this.currentUnit = 0;

        return 0;
    },

    /**
     * Custom pipelines can use this method in order to perform any required pre-batch tasks
     * for the given Game Object. It must return the texture unit the Game Object was assigned.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setGameObject
     * @since 3.25.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object being rendered or added to the batch.
     * @param {Phaser.Textures.Frame} [frame] - Optional frame to use. Can override that of the Game Object.
     *
     * @return {number} The texture unit the Game Object has been assigned.
     */
    setGameObject: function (gameObject, frame)
    {
        if (frame === undefined) { frame = gameObject.frame; }

        var renderer = this.renderer;
        var texture = frame.glTexture;
        var normalTexture = this.getNormalMap(gameObject);

        if (renderer.isNewNormalMap())
        {
            this.flush();

            renderer.setTextureZero(texture);
            renderer.setNormalMap(normalTexture);
        }

        this.setNormalMapRotation(gameObject.rotation);

        this.currentUnit = 0;

        return 0;
    },

    /**
     * Returns the normal map WebGLTexture from the given Game Object.
     * If the Game Object doesn't have one, it returns the default normal map from this pipeline instead.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#getNormalMap
     * @since 3.25.0
     *
     * @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object to get the normal map from.
     *
     * @return {WebGLTexture} The normal map texture.
     */
    getNormalMap: function (gameObject)
    {
        var normalTexture;

        if (!gameObject)
        {
            normalTexture = this.defaultNormalMap;
        }
        else if (gameObject.displayTexture)
        {
            normalTexture = gameObject.displayTexture.dataSource[gameObject.displayFrame.sourceIndex];
        }
        else if (gameObject.texture)
        {
            normalTexture = gameObject.texture.dataSource[gameObject.frame.sourceIndex];
        }
        else if (gameObject.tileset)
        {
            if (Array.isArray(gameObject.tileset))
            {
                normalTexture = gameObject.tileset[0].image.dataSource[0];
            }
            else
            {
                normalTexture = gameObject.tileset.image.dataSource[0];
            }
        }

        if (!normalTexture)
        {
            normalTexture = this.defaultNormalMap;
        }

        return normalTexture.glTexture;
    }

});

ForwardDiffuseLightPipeline.LIGHT_COUNT = LIGHT_COUNT;

module.exports = ForwardDiffuseLightPipeline;
<commit_msg>Always set rotation<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @author       Felipe Alfonso <@bitnenfer>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../../utils/Class');
var ShaderSourceFS = require('../shaders/ForwardDiffuse-frag.js');
var TextureTintPipeline = require('./TextureTintPipeline');
var WebGLPipeline = require('../WebGLPipeline');

var LIGHT_COUNT = 10;

/**
 * @classdesc
 * ForwardDiffuseLightPipeline implements a forward rendering approach for 2D lights.
 * This pipeline extends TextureTintPipeline so it implements all it's rendering functions
 * and batching system.
 *
 * @class ForwardDiffuseLightPipeline
 * @extends Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline
 * @memberof Phaser.Renderer.WebGL.Pipelines
 * @constructor
 * @since 3.0.0
 *
 * @param {object} config - The configuration of the pipeline, same as the {@link Phaser.Renderer.WebGL.Pipelines.TextureTintPipeline}. The fragment shader will be replaced with the lighting shader.
 */
var ForwardDiffuseLightPipeline = new Class({

    Extends: TextureTintPipeline,

    initialize:

    function ForwardDiffuseLightPipeline (config)
    {
        LIGHT_COUNT = config.maxLights;

        config.fragShader = ShaderSourceFS.replace('%LIGHT_COUNT%', LIGHT_COUNT.toString());

        TextureTintPipeline.call(this, config);

        /**
         * Inverse rotation matrix for normal map rotations.
         *
         * @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#inverseRotationMatrix
         * @type {Float32Array}
         * @private
         * @since 3.16.0
         */
        this.inverseRotationMatrix = new Float32Array([
            1, 0, 0,
            0, 1, 0,
            0, 0, 1
        ]);

        /**
         * Stores a default normal map, which is an object with a `glTexture` property that
         * maps to a 1x1 texture of the color #7f7fff created in the `boot` method.
         *
         * @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#defaultNormalMap
         * @type {object}
         * @since 3.25.0
         */
        this.defaultNormalMap;

        /**
         * Stores the previous number of lights rendered.
         *
         * @name Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#lightCount
         * @type {number}
         * @since 3.25.0
         */
        this.lightCount = 0;

        this.forceZero = true;
    },

    /**
     * Called when the Game has fully booted and the Renderer has finished setting up.
     *
     * By this stage all Game level systems are now in place and you can perform any final
     * tasks that the pipeline may need that relied on game systems such as the Texture Manager.
     *
     * @method Phaser.Renderer.WebGL.ForwardDiffuseLightPipeline#boot
     * @since 3.11.0
     */
    boot: function ()
    {
        WebGLPipeline.prototype.boot.call(this);

        var gl = this.gl;

        var tempTexture = gl.createTexture();

        gl.activeTexture(gl.TEXTURE0);

        gl.bindTexture(gl.TEXTURE_2D, tempTexture);

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([ 127, 127, 255, 255 ]));

        this.defaultNormalMap = { glTexture: tempTexture };

        return this;
    },

    /**
     * Called every time the pipeline is bound by the renderer.
     * Sets the shader program, vertex buffer and other resources.
     * Should only be called when changing pipeline.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#bind
     * @since 3.25.0
     *
     * @return {this} This WebGLPipeline instance.
     */
    bind: function ()
    {
        WebGLPipeline.prototype.bind.call(this);

        var renderer = this.renderer;
        var program = this.program;

        renderer.setInt1(program, 'uMainSampler', 0);
        renderer.setInt1(program, 'uNormSampler', 1);
        renderer.setFloat2(program, 'uResolution', this.width, this.height);

        return this;
    },

    /**
     * This function sets all the needed resources for each camera pass.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#onRender
     * @since 3.0.0
     *
     * @param {Phaser.Scene} scene - The Scene being rendered.
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Scene Camera being rendered with.
     *
     * @return {this} This WebGLPipeline instance.
     */
    onRender: function (scene, camera)
    {
        this.active = false;

        var lightManager = scene.sys.lights;

        if (!lightManager || lightManager.lights.length <= 0 || !lightManager.active)
        {
            //  Passthru
            return this;
        }

        var lights = lightManager.cull(camera);
        var lightCount = Math.min(lights.length, LIGHT_COUNT);

        if (lightCount === 0)
        {
            return this;
        }

        this.active = true;

        var renderer = this.renderer;
        var program = this.program;
        var cameraMatrix = camera.matrix;
        var point = {x: 0, y: 0};
        var height = renderer.height;
        var i;

        if (lightCount !== this.lightCount)
        {
            for (i = 0; i < LIGHT_COUNT; i++)
            {
                //  Reset lights
                renderer.setFloat1(program, 'uLights[' + i + '].radius', 0);
            }

            this.lightCount = lightCount;
        }

        if (camera.dirty)
        {
            renderer.setFloat4(program, 'uCamera', camera.x, camera.y, camera.rotation, camera.zoom);
        }

        //  TODO - Only if dirty! and cache the location
        renderer.setFloat3(program, 'uAmbientLightColor', lightManager.ambientColor.r, lightManager.ambientColor.g, lightManager.ambientColor.b);

        for (i = 0; i < lightCount; i++)
        {
            var light = lights[i];
            var lightName = 'uLights[' + i + '].';

            cameraMatrix.transformPoint(light.x, light.y, point);

            //  TODO - Cache the uniform locations!!!
            renderer.setFloat2(program, lightName + 'position', point.x - (camera.scrollX * light.scrollFactorX * camera.zoom), height - (point.y - (camera.scrollY * light.scrollFactorY) * camera.zoom));

            if (light.dirty)
            {
                renderer.setFloat3(program, lightName + 'color', light.r, light.g, light.b);
                renderer.setFloat1(program, lightName + 'intensity', light.intensity);
                renderer.setFloat1(program, lightName + 'radius', light.radius);
                light.dirty = false;
            }
        }

        this.currentNormalMapRotation = null;

        return this;
    },

    /**
     * Rotates the normal map vectors inversely by the given angle.
     * Only works in 2D space.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setNormalMapRotation
     * @since 3.16.0
     *
     * @param {number} rotation - The angle of rotation in radians.
     */
    setNormalMapRotation: function (rotation)
    {
        if (rotation !== this.currentNormalMapRotation || this.vertexCount === 0)
        {
            if (this.vertexCount > 0)
            {
                this.flush();
            }

            var inverseRotationMatrix = this.inverseRotationMatrix;

            if (rotation)
            {
                var rot = -rotation;
                var c = Math.cos(rot);
                var s = Math.sin(rot);

                inverseRotationMatrix[1] = s;
                inverseRotationMatrix[3] = -s;
                inverseRotationMatrix[0] = inverseRotationMatrix[4] = c;
            }
            else
            {
                inverseRotationMatrix[0] = inverseRotationMatrix[4] = 1;
                inverseRotationMatrix[1] = inverseRotationMatrix[3] = 0;
            }

            this.renderer.setMatrix3(this.program, 'uInverseRotationMatrix', false, inverseRotationMatrix);

            this.currentNormalMapRotation = rotation;
        }
    },

    /**
     * Assigns a texture to the current batch. If a different texture is already set it creates a new batch object.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setTexture2D
     * @since 3.25.0
     *
     * @param {WebGLTexture} [texture] - WebGLTexture that will be assigned to the current batch. If not given uses blankTexture.
     * @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object being rendered or added to the batch.
     */
    setTexture2D: function (texture, gameObject)
    {
        var renderer = this.renderer;

        if (texture === undefined) { texture = renderer.tempTextures[0]; }

        var normalTexture = this.getNormalMap(gameObject);

        if (renderer.isNewNormalMap())
        {
            this.flush();

            renderer.setTextureZero(texture);
            renderer.setNormalMap(normalTexture);
        }

        var rotation = (gameObject) ? gameObject.rotation : 0;

        this.setNormalMapRotation(rotation);

        this.currentUnit = 0;

        return 0;
    },

    /**
     * Custom pipelines can use this method in order to perform any required pre-batch tasks
     * for the given Game Object. It must return the texture unit the Game Object was assigned.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#setGameObject
     * @since 3.25.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object being rendered or added to the batch.
     * @param {Phaser.Textures.Frame} [frame] - Optional frame to use. Can override that of the Game Object.
     *
     * @return {number} The texture unit the Game Object has been assigned.
     */
    setGameObject: function (gameObject, frame)
    {
        if (frame === undefined) { frame = gameObject.frame; }

        var renderer = this.renderer;
        var texture = frame.glTexture;
        var normalTexture = this.getNormalMap(gameObject);

        if (renderer.isNewNormalMap())
        {
            this.flush();

            renderer.setTextureZero(texture);
            renderer.setNormalMap(normalTexture);
        }

        this.setNormalMapRotation(gameObject.rotation);

        this.currentUnit = 0;

        return 0;
    },

    /**
     * Returns the normal map WebGLTexture from the given Game Object.
     * If the Game Object doesn't have one, it returns the default normal map from this pipeline instead.
     *
     * @method Phaser.Renderer.WebGL.Pipelines.ForwardDiffuseLightPipeline#getNormalMap
     * @since 3.25.0
     *
     * @param {Phaser.GameObjects.GameObject} [gameObject] - The Game Object to get the normal map from.
     *
     * @return {WebGLTexture} The normal map texture.
     */
    getNormalMap: function (gameObject)
    {
        var normalTexture;

        if (!gameObject)
        {
            normalTexture = this.defaultNormalMap;
        }
        else if (gameObject.displayTexture)
        {
            normalTexture = gameObject.displayTexture.dataSource[gameObject.displayFrame.sourceIndex];
        }
        else if (gameObject.texture)
        {
            normalTexture = gameObject.texture.dataSource[gameObject.frame.sourceIndex];
        }
        else if (gameObject.tileset)
        {
            if (Array.isArray(gameObject.tileset))
            {
                normalTexture = gameObject.tileset[0].image.dataSource[0];
            }
            else
            {
                normalTexture = gameObject.tileset.image.dataSource[0];
            }
        }

        if (!normalTexture)
        {
            normalTexture = this.defaultNormalMap;
        }

        return normalTexture.glTexture;
    }

});

ForwardDiffuseLightPipeline.LIGHT_COUNT = LIGHT_COUNT;

module.exports = ForwardDiffuseLightPipeline;
<|endoftext|>