text: /*!
   * jQuery-Seat-Charts v1.1.5
   * https://github.com/mateuszmarkowski/jQuery-Seat-Charts
   *
   * Copyright 2013, 2016 Mateusz Markowski
   * Released under the MIT license
   */
  
  (function($) {
  		
  	//'use strict';	
  		
  	$.fn.seatCharts = function (setup) {
  
  		//if there's seatCharts object associated with the current element, return it
  		if (this.data('seatCharts')) {
  			return this.data('seatCharts');
  		}
  		
  		var fn       = this,
  			seats    = {},
  			seatIds  = [],
  			legend,
  			settings = {
  				animate : false, //requires jQuery UI
  				naming  : {
  					top    : true,
  					left   : true,
  					getId  : function(character, row, column) {
  						return row + '_' + column;
  					},
  					getLabel : function (character, row, column) {
  						return column;
  					}
  					
  				},
  				legend : {
  					node   : null,
  					items  : []
  				},
  				click   : function() {
  
  					if (this.status() == 'available') {
  						return 'selected';
  					} else if (this.status() == 'selected') {
  						return 'available';
  					} else {
  						return this.style();
  					}
  					
  				},
  				focus  : function() {
  
  					if (this.status() == 'available') {
  						return 'focused';
  					} else  {
  						return this.style();
  					}
  				},
  				blur   : function() {
  					return this.status();
  				},
  				seats   : {}
                  
  			},
  			//seat will be basically a seat object which we'll when generating the map
  			seat = (function(seatCharts, seatChartsSettings) {
  				return function (setup) {
  					var fn = this;
  					
  					fn.settings = $.extend({
  						status : 'available', //available, unavailable, selected
  						style  : 'available',
  						//make sure there's an empty hash if user doesn't pass anything
  						data   : seatChartsSettings.seats[setup.character] || {}
  						//anything goes here?
  					}, setup);
  
  					fn.settings.$node = $('<div></div>');
  					
  					fn.settings.$node
  						.attr({
  							id             : fn.settings.id,
  							role           : 'checkbox',
  							'aria-checked' : false,
  							focusable      : true,
  							tabIndex       : -1 //manual focus
  						})
  						.text(fn.settings.label)
  						.addClass(['seatCharts-seat', 'seatCharts-cell', 'available'].concat(
  							//let's merge custom user defined classes with standard JSC ones
  							fn.settings.classes, 
  							typeof seatChartsSettings.seats[fn.settings.character] == "undefined" ? 
  								[] : seatChartsSettings.seats[fn.settings.character].classes
  							).join(' '));
  					
  					//basically a wrapper function
  					fn.data = function() {
  						return fn.settings.data;
  					};
  					
  					fn.char = function() {
  						return fn.settings.character;
  					};
  					
  					fn.node = function() {
  						return fn.settings.$node;						
  					};
  
  					/*
  					 * Can either set or return status depending on arguments.
  					 *
  					 * If there's no argument, it will return the current style.
  					 *
  					 * If you pass an argument, it will update seat's style
  					 */
  					fn.style = function() {
  
  						return arguments.length == 1 ?
  							(function(newStyle) {
  								var oldStyle = fn.settings.style;
  
  								//if nothing changes, do nothing
  								if (newStyle == oldStyle) {
  									return oldStyle;
  								}
  								
  								//focused is a special style which is not associated with status
  								fn.settings.status = newStyle != 'focused' ? newStyle : fn.settings.status;
  								fn.settings.$node
  									.attr('aria-checked', newStyle == 'selected');
  
  								//if user wants to animate status changes, let him do this
  								seatChartsSettings.animate ?
  									fn.settings.$node.switchClass(oldStyle, newStyle, 200) :
  									fn.settings.$node.removeClass(oldStyle).addClass(newStyle);
  									
  								return fn.settings.style = newStyle;
  							})(arguments[0]) : fn.settings.style;
  					};
  					
  					//either set or retrieve
  					fn.status = function() {
  	
  						return fn.settings.status = arguments.length == 1 ? 
  							fn.style(arguments[0]) : fn.settings.status;
  					};
  					
  					//using immediate function to convienietly get shortcut variables
  					(function(seatSettings, character, seat) {
  						//attach event handlers
  						$.each(['click', 'focus', 'blur'], function(index, callback) {
  						
  							//we want to be able to call the functions for each seat object
  							fn[callback] = function() {
  								if (callback == 'focus') {
  									//if there's already a focused element, we have to remove focus from it first
  									if (seatCharts.attr('aria-activedescendant') !== undefined) {
  										seats[seatCharts.attr('aria-activedescendant')].blur();
  									}
  									seatCharts.attr('aria-activedescendant', seat.settings.id);
  									seat.node().focus();
  								}
  							
  								/*
  								 * User can pass his own callback function, so we have to first check if it exists
  								 * and if not, use our default callback.
  								 *
  								 * Each callback function is executed in the current seat context.
  								 */
  								return fn.style(typeof seatSettings[character][callback] === 'function' ?
  									seatSettings[character][callback].apply(seat) : seatChartsSettings[callback].apply(seat));
  							};
  							
  						});
  					//the below will become seatSettings, character, seat thanks to the immediate function		
  					})(seatChartsSettings.seats, fn.settings.character, fn);
  							
  					fn.node()
  						//the first three mouse events are simple
  						.on('click',      fn.click)
  						.on('mouseenter', fn.focus)
  						.on('mouseleave', fn.blur)
  						
  						//keydown requires quite a lot of logic, because we have to know where to move the focus
  						.on('keydown',    (function(seat, $seat) {
  						
  							return function (e) {
  								
  								var $newSeat;
  								
  								//everything depends on the pressed key
  								switch (e.which) {
  									//spacebar will just trigger the same event mouse click does
  									case 32:
  										e.preventDefault();
  										seat.click();
  										break;
  									//UP & DOWN
  									case 40:
  									case 38:
  										e.preventDefault();
  										
  										/*
  										 * This is a recursive, immediate function which searches for the first "focusable" row.
  										 * 
  										 * We're using immediate function because we want a convenient access to some DOM elements
  										 * We're using recursion because sometimes we may hit an empty space rather than a seat.
  										 *
  										 */
  										$newSeat = (function findAvailable($rows, $seats, $currentRow) {
  											var $newRow;
  											
  											//let's determine which row should we move to
  											
  											if (!$rows.index($currentRow) && e.which == 38) {
  												//if this is the first row and user has pressed up arrow, move to the last row
  												$newRow = $rows.last();
  											} else if ($rows.index($currentRow) == $rows.length-1 && e.which == 40) {
  												//if this is the last row and user has pressed down arrow, move to the first row
  												$newRow = $rows.first();
  											} else {
  												//using eq to get an element at the desired index position
  												$newRow = $rows.eq(
  													//if up arrow, then decrement the index, if down increment it
  													$rows.index($currentRow) + (e.which == 38 ? (-1) : (+1))
  												);
  											}												
  											
  											//now that we know the row, let's get the seat using the current column position
  											$newSeat = $newRow.find('.seatCharts-seat,.seatCharts-space').eq($seats.index($seat));
  											
  											//if the seat we found is a space, keep looking further
  											return $newSeat.hasClass('seatCharts-space') ?
  												findAvailable($rows, $seats, $newRow) : $newSeat;
  											
  										})($seat
  											//get a reference to the parent container and then select all rows but the header
  												.parents('.seatCharts-container')
  												.find('.seatCharts-row:not(.seatCharts-header)'),
  											$seat
  											//get a reference to the parent row and then find all seat cells (both seats & spaces)
  												.parents('.seatCharts-row:first')
  												.find('.seatCharts-seat,.seatCharts-space'),
  											//get a reference to the current row
  											$seat.parents('.seatCharts-row:not(.seatCharts-header)')
  										);
  										
  										//we couldn't determine the new seat, so we better give up
  										if (!$newSeat.length) {
  											return;
  										}
  										
  										//remove focus from the old seat and put it on the new one
  										seat.blur();
  										seats[$newSeat.attr('id')].focus();
  										$newSeat.focus();
  										
  										//update our "aria" reference with the new seat id
  										seatCharts.attr('aria-activedescendant', $newSeat.attr('id'));
  																			
  										break;										
  									//LEFT & RIGHT
  									case 37:
  									case 39:
  										e.preventDefault();
  										/*
  										 * The logic here is slightly different from the one for up/down arrows.
  										 * User will be able to browse the whole map using just left/right arrow, because
  										 * it will move to the next row when we reach the right/left-most seat.
  										 */
  										$newSeat = (function($seats) {
  										
  											if (!$seats.index($seat) && e.which == 37) {
  												//user has pressed left arrow and we're currently on the left-most seat
  												return $seats.last();
  											} else if ($seats.index($seat) == $seats.length -1 && e.which == 39) {
  												//user has pressed right arrow and we're currently on the right-most seat
  												return $seats.first();
  											} else {
  												//simply move one seat left or right depending on the key
  												return $seats.eq($seats.index($seat) + (e.which == 37 ? (-1) : (+1)));
  											}
  
  										})($seat
  											.parents('.seatCharts-container:first')
  											.find('.seatCharts-seat:not(.seatCharts-space)'));
  										
  										if (!$newSeat.length) {
  											return;
  										}
  											
  										//handle focus
  										seat.blur();	
  										seats[$newSeat.attr('id')].focus();
  										$newSeat.focus();
  										
  										//update our "aria" reference with the new seat id
  										seatCharts.attr('aria-activedescendant', $newSeat.attr('id'));
  										break;	
  									default:
  										break;
  								
  								}
  							};
  								
  						})(fn, fn.node()));
  						//.appendTo(seatCharts.find('.' + row));
  
  				}
  			})(fn, settings);
  			
  		fn.addClass('seatCharts-container');
  		
  		//true -> deep copy!
  		$.extend(true, settings, setup);		
  		
  		//Generate default row ids unless user passed his own
  		settings.naming.rows = settings.naming.rows || (function(length) {
  			var rows = [];
  			for (var i = 1; i <= length; i++) {
  				rows.push(i);
  			}
  			return rows;
  		})(settings.map.length);
  		
  		//Generate default column ids unless user passed his own
  		settings.naming.columns = settings.naming.columns || (function(length) {
  			var columns = [];
  			for (var i = 1; i <= length; i++) {
  				columns.push(i);
  			}
  			return columns;
  		})(settings.map[0].split('').length);
  		
  		if (settings.naming.top) {
  			var $headerRow = $('<div></div>')
  				.addClass('seatCharts-row seatCharts-header');
  			
  			if (settings.naming.left) {
  				$headerRow.append($('<div></div>').addClass('seatCharts-cell'));
  			}
  			
  				
  			$.each(settings.naming.columns, function(index, value) {
  				$headerRow.append(
  					$('<div></div>')
  						.addClass('seatCharts-cell')
  						.text(value)
  				);
  			});
  		}
  		
  		fn.append($headerRow);
  		
  		//do this for each map row
  		$.each(settings.map, function(row, characters) {
  
  			var $row = $('<div></div>').addClass('seatCharts-row');
  				
  			if (settings.naming.left) {
  				$row.append(
  					$('<div></div>')
  						.addClass('seatCharts-cell seatCharts-space')
  						.text(settings.naming.rows[row])
  				);
  			}
  
  			/*
  			 * Do this for each seat (letter)
  			 *
  			 * Now users will be able to pass custom ID and label which overwrite the one that seat would be assigned by getId and
  			 * getLabel
  			 *
  			 * New format is like this:
  			 * a[ID,label]a[ID]aaaaa
  			 *
  			 * So you can overwrite the ID or label (or both) even for just one seat.
  			 * Basically ID should be first, so if you want to overwrite just label write it as follows:
  			 * a[,LABEL]
  			 *
  			 * Allowed characters in IDs areL 0-9, a-z, A-Z, _
  			 * Allowed characters in labels are: 0-9, a-z, A-Z, _, ' ' (space)
  			 *
  			 */
  			 
  			$.each(characters.match(/[a-z_]{1}(\[[0-9a-z_]{0,}(,[0-9a-z_ ]+)?\])?/gi), function (column, characterParams) { 
  				var matches         = characterParams.match(/([a-z_]{1})(\[([0-9a-z_ ,]+)\])?/i),
  					//no matter if user specifies [] params, the character should be in the second element
  					character       = matches[1],
  					//check if user has passed some additional params to override id or label
  					params          = typeof matches[3] !== 'undefined' ? matches[3].split(',') : [],
  					//id param should be first
  					overrideId      = params.length ? params[0] : null,
  					//label param should be second
  					overrideLabel   = params.length === 2 ? params[1] : null;
  								
  				$row.append(character != '_' ?
  					//if the character is not an underscore (empty space)
  					(function(naming) {
  	
  						//so users don't have to specify empty objects
  						settings.seats[character] = character in settings.seats ? settings.seats[character] : {};
  	
  						var id = overrideId ? overrideId : naming.getId(character, naming.rows[row], naming.columns[column]);
  						seats[id] = new seat({
  							id        : id,
  							label     : overrideLabel ?
  								overrideLabel : naming.getLabel(character, naming.rows[row], naming.columns[column]),
  							row       : row,
  							column    : column,
  							character : character
  						});
  
  						seatIds.push(id);
  						return seats[id].node();
  						
  					})(settings.naming) :
  					//this is just an empty space (_)
  					$('<div></div>').addClass('seatCharts-cell seatCharts-space')	
  				);
  			});
  			
  			fn.append($row);
  		});
  	
  		//if there're any legend items to be rendered
  		settings.legend.items.length ? (function(legend) {
  			//either use user-defined container or create our own and insert it right after the seat chart div
  			var $container = (legend.node || $('<div></div>').insertAfter(fn))
  				.addClass('seatCharts-legend');
  				
  			var $ul = $('<ul></ul>')
  				.addClass('seatCharts-legendList')
  				.appendTo($container);
  			
  			$.each(legend.items, function(index, item) {
  				$ul.append(
  					$('<li></li>')
  						.addClass('seatCharts-legendItem')
  						.append(
  							$('<div></div>')
  								//merge user defined classes with our standard ones
  								.addClass(['seatCharts-seat', 'seatCharts-cell', item[1]].concat(
  									settings.classes, 
  									typeof settings.seats[item[0]] == "undefined" ? [] : settings.seats[item[0]].classes).join(' ')
  								)
  						)
  						.append(
  							$('<span></span>')
  								.addClass('seatCharts-legendDescription')
  								.text(item[2])
  						)
  				);
  			});
  			
  			return $container;
  		})(settings.legend) : null;
  	
  		fn.attr({
  			tabIndex : 0
  		});
  		
  		
  		//when container's focused, move focus to the first seat
  		fn.focus(function() {
  			if (fn.attr('aria-activedescendant')) {
  				seats[fn.attr('aria-activedescendant')].blur();
  			}
  				
  			fn.find('.seatCharts-seat:not(.seatCharts-space):first').focus();
  			seats[seatIds[0]].focus();
  
  		});
  	
  		//public methods of seatCharts
  		fn.data('seatCharts', {
  			seats   : seats,
  			seatIds : seatIds,
  			//set for one, set for many, get for one
  			status: function() {
  				var fn = this;
  			
  				return arguments.length == 1 ? fn.seats[arguments[0]].status() : (function(seatsIds, newStatus) {
  				
  					return typeof seatsIds == 'string' ? fn.seats[seatsIds].status(newStatus) : (function() {
  						$.each(seatsIds, function(index, seatId) {
  							fn.seats[seatId].status(newStatus);
  						});
  					})();
  				})(arguments[0], arguments[1]);
  			},
  			each  : function(callback) {
  				var fn = this;
  			
  				for (var seatId in fn.seats) {
  					if (false === callback.call(fn.seats[seatId], seatId)) {
  						return seatId;//return last checked
  					}
  				}
  				
  				return true;
  			},
  			node       : function() {
  				var fn = this;
  				//basically create a CSS query to get all seats by their DOM ids
  				return $('#' + fn.seatIds.join(',#'));
  			},
  
  			find       : function(query) {//D, a.available, unavailable
  				var fn = this;
  			
  				var seatSet = fn.set();
  			
  				//is RegExp
  		                return query instanceof RegExp ?
  		                    (function () {
  		                        fn.each(function (id) {
  		                            if (id.match(query)) {
  		                                seatSet.push(id, this);
  		                            }
  		                        });
  		                        return seatSet;
  		                    })() :
  		                    (query.length == 1 ?
  		                            (function (character) {
  		                                //user searches just for a particual character
  		                                fn.each(function () {
  		                                    if (this.char() == character) {
  		                                        seatSet.push(this.settings.id, this);
  		                                    }
  		                                });
  		
  		                                return seatSet;
  		                            })(query) :
  		                            (function () {
  		                                //user runs a more sophisticated query, so let's see if there's a dot
  		                                return query.indexOf('.') > -1 ?
  		                                    (function () {
  		                                        //there's a dot which separates character and the status
  		                                        var parts = query.split('.');
  		
  		                                        fn.each(function (seatId) {
  		                                            if (this.char() == parts[0] && this.status() == parts[1]) {
  		                                                seatSet.push(this.settings.id, this);
  		                                            }
  		                                        });
  		
  		                                        return seatSet;
  		                                    })() :
  		                                    (function () {
  		                                        fn.each(function () {
  		                                            if (this.status() == query) {
  		                                                seatSet.push(this.settings.id, this);
  		                                            }
  		                                        });
  		                                        return seatSet;
  		                                    })();
  		                            })()
  		                    );
  				
  			},
  			set        : function set() {//inherits some methods
  				var fn = this;
  				
  				return {
  					seats      : [],
  					seatIds    : [],
  					length     : 0,
  					status     : function() {
  						var args = arguments,
  							that = this;
  						//if there's just one seat in the set and user didn't pass any params, return current status
  						return this.length == 1 && args.length == 0 ? this.seats[0].status() : (function() {
  							//otherwise call status function for each of the seats in the set
  							$.each(that.seats, function() {
  								this.status.apply(this, args);
  							});
  						})();
  					},
  					node       : function() {
  						return fn.node.call(this);
  					},
  					each       : function() {
  						return fn.each.call(this, arguments[0]);
  					},
  					get        : function() {
  						return fn.get.call(this,arguments[0]);
  					},
  					find       : function() {
  						return fn.find.call(this, arguments[0]);
  					},
  					set       : function() {
  						return set.call(fn);
  					},
  					push       : function(id, seat) {
  						this.seats.push(seat);
  						this.seatIds.push(id);
  						++this.length;
  					}
  				};
  			},
  			//get one object or a set of objects
  			get   : function(seatsIds) {
  				
  				var fn = this;
  
  				return typeof seatsIds == 'string' ? 
  					fn.seats[seatsIds] : (function() {
  						
  						var seatSet = fn.set();
  						
  						$.each(seatsIds, function(index, seatId) {
  							if (typeof fn.seats[seatId] === 'object') {
  								seatSet.push(seatId, fn.seats[seatId]);
  							}
  						});
  						
  						return seatSet;
  					})();
  			}
  		});
  		
  		return fn.data('seatCharts');
  	}
  	
  	
  })(jQuery);
  