Compare commits

...

50 Commits

Author SHA1 Message Date
root
01c946db63 npm run prod 2022-12-27 11:15:03 +01:00
mmaschkiwitz
63fda7458d re-added cssjbox, need to cleanly remove for npm run prod/dev to build 2022-12-21 15:59:13 +01:00
mmaschkiwitz
5471248ac3 removed cssjbox config value 2022-12-21 12:55:36 +01:00
mmaschkiwitz
ee048efb59 replaced host zinomedia with provenue in config 2022-12-21 12:55:00 +01:00
root
c057d3b3a6 added jquery lib natively. Note: would be better to use git submodules. 2022-12-21 12:44:27 +01:00
root
9ddc28c0a0 remove jquery lib 2022-12-21 12:43:21 +01:00
zino
6e38da3a95 npm run prod 2022-10-10 14:19:45 +02:00
zino
c3d9517570 manually changed fallback color 2022-10-10 14:18:58 +02:00
zino
d514621ed3 increased version number 2022-10-10 14:16:33 +02:00
zino
305a9ad0ca npm run prod 2022-09-20 15:44:37 +02:00
mmaschkiwitz
e06fa480dc Merge pull request 'staging' (#1) from staging into master
Reviewed-on: https://gitea.provenue.de/Tickets.com/seatmapv2/pulls/1
2022-09-20 15:28:46 +02:00
zino
fa0222f1c6 removed dist 2022-09-20 15:26:16 +02:00
zino
8126d7be13 Added feature to specify first seatmap on load by defining second value of SMAP (must be valid code). If invalid code given, first seatmap will be returned. 2022-09-20 14:45:48 +02:00
zino
f959ad27f5 Merge pull request 'staging' (#20) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/20
2021-09-30 16:09:44 +02:00
zino
468f0a46b0 added coupon successfull message to prevent inject 2021-09-30 16:07:41 +02:00
zino
27aa67889f added coupon code box inject dependency 2021-09-30 14:57:49 +02:00
zino
8153040f0e Merge pull request 'staging' (#19) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/19
2021-08-04 19:42:25 +02:00
zino
e707d378cc whitespace removal 2021-08-04 19:24:09 +02:00
zino
55498c9ee6 Merge pull request 'fixed sort highest amount: needed parseFloat for right sort, were strings before' (#18) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/18
2021-08-04 17:47:51 +02:00
zino
d1f2afaccf Merge pull request 'added border to seatmap unavailable and available cells to make white seats visible' (#17) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/17
2021-08-04 15:33:47 +02:00
zino
6bb67a1f4f Merge pull request 'staging' (#16) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/16
2021-07-28 16:45:21 +02:00
zino
d432d8c0a9 Merge pull request 'sorted buyer types in cart popup highest price first' (#15) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/15
2021-07-28 10:13:30 +02:00
zino
2558178994 Merge pull request 'staging' (#14) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/14
2021-06-10 14:19:37 +02:00
zino
a0a41ca3eb merge && npm run prod 2021-05-31 16:16:25 +02:00
zino
5813b84fc5 Merge pull request 'staging' (#12) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/12
2021-05-31 16:13:40 +02:00
zino
ad7d867600 npm run prod 2021-05-28 17:41:02 +02:00
zino
48f9cfa321 Merge pull request 'staging' (#10) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/10
2021-05-28 17:38:48 +02:00
zino
25d2c51328 npm run prod 2021-05-28 12:20:44 +02:00
zino
8f1b7bb786 Merge pull request 'staging' (#8) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/8
2021-05-28 12:17:46 +02:00
zino
3b45932549 npm run prod 2021-05-27 16:16:35 +02:00
zino
493cd6852f changed branch to master after resolving conflict 2021-05-27 16:14:28 +02:00
zino
6915b831cf Merge remote-tracking branch 'origin/staging' 2021-05-27 16:07:05 +02:00
zino
d4af93a44a npm run prod 2021-05-27 16:03:41 +02:00
zino
5aaaa50dbe Merge branches 'master' and 'master' of ssh://gitea/zino/seatmapv2 2021-05-27 15:57:39 +02:00
zino
c0db2e3251 Merge pull request 'staging' (#7) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/7
2021-05-27 15:56:30 +02:00
zino
9499f2d5d7 Merge branch 'master' of ssh://gitea/zino/seatmapv2 2021-05-24 14:40:09 +02:00
zino
456ac53443 Merge pull request 'staging' (#6) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/6
2021-05-24 14:39:53 +02:00
zino
6339c51235 Merge remote-tracking branch 'origin/staging' 2021-05-24 14:37:20 +02:00
zino
9896fa1488 npm run prod 2021-05-23 23:02:55 +02:00
zino
2ed30c5630 Merge remote-tracking branch 'origin/staging' 2021-05-23 23:00:50 +02:00
zino
d80ac6a19f Merge remote-tracking branch 'origin/staging' 2021-05-23 22:44:57 +02:00
zino
35f914f938 gitattributes not needed 2021-05-23 19:51:05 +02:00
zino
e73ecc8747 Merge pull request 'Update 'client/tsconfig.json'' (#4) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/4
2021-05-23 02:04:43 +02:00
zino
99752b9e32 Merge branch 'master' of ssh://gitea/zino/seatmapv2 2021-05-23 02:01:16 +02:00
zino
22e5afeb3f added gitattributes and driver ours 2021-05-23 02:01:07 +02:00
zino
91826bd305 Merge pull request 'staging' (#2) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/2
2021-05-23 01:59:38 +02:00
zino
500d23b117 Merge branch 'staging' of ssh://gitea/zino/seatmapv2 2021-05-21 21:58:32 +02:00
zino
e45825a9dc Merge remote-tracking branch 'origin/staging' 2021-05-21 21:44:03 +02:00
zino
4f2ded59cb minify true 2021-05-21 21:22:30 +02:00
zino
27c4e2043f Merge pull request 'staging' (#1) from staging into master
Reviewed-on: http://localhost:3000/zino/seatmapv2/pulls/1
2021-05-21 00:25:40 +02:00
23 changed files with 6175 additions and 3025 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
#package.json merge=ours

1
client/dist/inject.css vendored Normal file
View File

@@ -0,0 +1 @@
#containerBookingBtn{display:none;margin:0;text-align:center}#get_flash{display:none}.ui-dialog-title{text-align:center;display:none}.ui-widget-overlay{background:#fff;opacity:1;width:100vw;height:100vh}#openSeatmap img{width:64px}#openSeatmap{padding:1rem!important;cursor:pointer;border-radius:5px}#openSeatmap span{font-size:1.2rem}#foobarParent{display:none}div#dialogSeatmap{padding-top:0}.ui-widget-header{background:#fff;border:1px solid #c6c6c6;border-bottom-right-radius:0;border-bottom-left-radius:0}.ui-dialog{background:#fff;left:0!important;padding:0;top:0!important;right:0!important}.ui-dialog .ui-dialog-content{padding:0}.ui-dialog-titlebar{margin-left:0;margin-right:0;padding:0!important;display:none}#iframeSeatmap{height:100vh;background:#fff;width:100%}.ui-corner-all{border-radius:0}.ui-widget{border:none!important}

1635
client/dist/inject.js vendored Normal file

File diff suppressed because one or more lines are too long

1
client/dist/seatmap.css vendored Normal file

File diff suppressed because one or more lines are too long

1635
client/dist/seatmap.js vendored Normal file

File diff suppressed because one or more lines are too long

3417
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,18 +15,21 @@ window.addEventListener('load', () => {
// Parse (hidden) inputs and their values
const content: string = new XMLSerializer().serializeToString(document);
inputsWithValue = { ...inputsWithValue, ...Parser.getInputs(content) };
if (jQuery("#get_flash").length) jQuery("#get_flash").hide();
Utils.consoleLog(inputsWithValue);
Utils.consoleLog(`trxstate: ${inputsWithValue["trxstate"]}`);
// "posturl" in function showMap() only available if "pvmapse" PVO code is true.
// "pvmapse" false disables seatmap
// "posturl" in function showMap() only available if "pvmapse" is true, false disables seatmap
const posturlAvail: boolean = /posturl:"(.+?)"/.test(content);
const couponCodeBoxAvail: boolean = !!jQuery("#coupon_code_box").length;
const couponSuccessfull: boolean = !!jQuery("#coupon_successful").length;
Utils.consoleLog(`couponCodeBoxAvail: ${couponCodeBoxAvail}`);
Utils.consoleLog(`posturl available: ${posturlAvail}`);
// Only inject on page with trxstate 20 and if seatmap is enabled with PVO code "pvmapse"
if (inputsWithValue["trxstate"] !== "20" || posturlAvail === false)
if (inputsWithValue["trxstate"] !== "20" || posturlAvail === false || couponCodeBoxAvail === true || couponSuccessfull === true)
return;
// Inject parent CSS

View File

@@ -37,7 +37,9 @@ export function initSendVenueXML(inE: any) {
UI.setOptionSelect(seatmapListing, "dropdownSeatmap");
// Display first seatmapXML
const id: string = seatmapListing[0].id[0];
// If smap[1] is given by user, display specific seatmap on load
const id: string = XMLHelper.getFirstSeatmapIDToLoad();
jQuery("#dropdownSeatmap").val(id);
Communication.needSeatmapXML(id);
@@ -67,7 +69,7 @@ export function sendSeatmapXML(inE: any) {
Legend.convertLegendToDropdown("dropdownLegend");
Events.dropdownLegendOnChange("#dropdownLegend");
Trims.addTrims();
XMLHelper.processSMAP();
XMLHelper.showHideVenueCapacity();
config.state.panzoom = Panzoom.addPanzoom("#containerSeatmapInner", ".panzoomZoomIn", ".panzoomZoomOut", "#panzoomResetZoom");
Cart.setImportantNote();
UI.controlLoftloader("hide");

View File

@@ -3,27 +3,27 @@ import * as I from "../types/types";
export const config: I.Config = {
childHasVenueXML: false,
debug: true,
branch: "staging",
version: "0.2.0",
branch: "master",
version: "0.2.1",
maxSelectedSeats: 10,
resources: {
master: {
seatmap: "https://tickets.zinomedia.de",
JSC: "https://tickets.zinomedia.de/libs/jQuery-Seat-Charts/jquery.seat-charts.min.js",
CSSChild: "https://tickets.zinomedia.de/dist/seatmap.css",
CSSJSC: "https://tickets.zinomedia.de/libs/jQuery-Seat-Charts/jquery.seat-charts.css",
CSSParent: "https://tickets.zinomedia.de/dist/inject.css",
seatmap: "https://tickets.provenue.de",
JSC: "https://tickets.provenue.de/libs/jQuery-Seat-Charts/jquery.seat-charts.min.js",
CSSChild: "https://tickets.provenue.de/dist/seatmap.css",
CSSJSC: "https://tickets.provenue.de/libs/jQuery-Seat-Charts/jquery.seat-charts.css",
CSSParent: "https://tickets.provenue.de/dist/inject.css",
CSSjQueryUI: "https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css",
CSSjBox: "https://tickets.zinomedia.de/dist/jBox.all.min.css"
CSSjBox: "https://tickets.provenue.de/dist/jBox.all.min.css"
},
staging: {
seatmap: "https://staging.tickets.zinomedia.de",
JSC: "https://staging.tickets.zinomedia.de/libs/jQuery-Seat-Charts/jquery.seat-charts.min.js",
CSSChild: "https://staging.tickets.zinomedia.de/dist/seatmap.css",
CSSJSC: "https://staging.tickets.zinomedia.de/libs/jQuery-Seat-Charts/jquery.seat-charts.css",
CSSParent: "https://staging.tickets.zinomedia.de/dist/inject.css",
seatmap: "https://staging.tickets.provenue.de",
JSC: "https://staging.tickets.provenue.de/libs/jQuery-Seat-Charts/jquery.seat-charts.min.js",
CSSChild: "https://staging.tickets.provenue.de/dist/seatmap.css",
CSSJSC: "https://staging.tickets.provenue.de/libs/jQuery-Seat-Charts/jquery.seat-charts.css",
CSSParent: "https://staging.tickets.provenue.de/dist/inject.css",
CSSjQueryUI: "https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css",
CSSjBox: "https://staging.tickets.zinomedia.de/dist/jBox.all.min.css"
CSSjBox: "https://tickets.provenue.de/dist/jBox.all.min.css"
}
},
state: {
@@ -42,7 +42,7 @@ export const config: I.Config = {
},
fallbackColors: [
"#ae2fb7",
"#150021",
"#f1c232",
"#90af80",
"#7f261a",
"#040fdb",

View File

@@ -18,8 +18,6 @@ export function convertLegendToDropdown(inID: string): void {
}
export function generateLegend(inNode: string): I.JSCLegend {
return <I.JSCLegend>{
node: jQuery(inNode),
items: createLegendItems()

View File

@@ -83,7 +83,10 @@ export function getSMAP(): string | undefined {
return undefined;
const str: string = jQuery("#seating_map_url a").attr("onclick");
const re = /openNewWindow\(\'(\d+)\'/;
//const re = /openNewWindow\(\'(\d+)\'/;
const re = /openNewWindow\(\'(.+?)\'/;
const found: RegExpMatchArray | null = str.match(re);
return found ? found[1] : undefined;

View File

@@ -1,19 +1,43 @@
import * as I from "../types/types";
import { config } from "./config";
export function processSMAP(): void {
export function showHideVenueCapacity(): void {
const inputsWithValue = config.state.inputsWithValue!;
if (!inputsWithValue.smap)
return;
const smapArr: number[] = inputsWithValue.smap.split("").map(Number);
// TODO: We are sure that there are always only two elements [number, string], so we can declare it as a tuple?
const smapArr = inputsWithValue.smap.split(";");
// Form:
// 0: = "0" -> Show venue capacity 1=yes 0=no
// 1: = "SC02" -> Show specific seat plan when loading for the first time
if (!smapArr[0])
// Convert first SMAP element to Number and hide or show venue capacity data
if (!Number(smapArr[0]))
jQuery("#eventInfoCapacity").hide();
}
export function getFirstSeatmapIDToLoad() {
const inputsWithValue = config.state.inputsWithValue!;
const venueXML = config.state.inVenueXML!;
const seatmapListing: I.Seatmap[] = venueXML.seatmap_config[0].seatmap;
const firstSeatmapID: string = seatmapListing[0].id[0];
// If SMAP field is missing, return ID of first seatmap
if (!inputsWithValue.smap)
return firstSeatmapID;
// If seatmap code is given in SMAP, return corresponding seatmap ID, otherwise return ID of first seatmap
const smapArr = inputsWithValue.smap.split(";"); // Form described above
if (!smapArr[1])
return firstSeatmapID;
const seatmapID: string | undefined = seatmapListing.find(arr => smapArr[1] === arr.code[0])?.id[0];
return seatmapID ? seatmapID : firstSeatmapID;
}
export function getSectionDescBySectionID(inSectionID: string): string | undefined {
const venueXML: I.VenueXML = config.state.inVenueXML!;
const sectionArr: I.Section[] = venueXML.master_config[0].section_config[0].section;

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013, 2016 Mateusz Markowski
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,528 @@
# jQuery Seat Charts
### jQuery Seat Charts (JSC) is a full-blown seat map library. It will generate an accessible map, legend, handle mouse & keyboard events and finally give you powerful selectors to control your map.
[Simple demo map](http://jsc.mm-lamp.com/)
## Example:
Basic setup:
$(document).ready(function() {
var sc = $('#seat-map').seatCharts({
map: [
'aaaaaaaaaaaa',
'aaaaaaaaaaaa',
'bbbbbbbbbb__',
'bbbbbbbbbb__',
'bbbbbbbbbbbb',
'cccccccccccc'
],
seats: {
a: {
price : 99.99,
classes : 'front-seat' //your custom CSS class
}
},
click: function () {
if (this.status() == 'available') {
//do some stuff, i.e. add to the cart
return 'selected';
} else if (this.status() == 'selected') {
//seat has been vacated
return 'available';
} else if (this.status() == 'unavailable') {
//seat has been already booked
return 'unavailable';
} else {
return this.style();
}
}
});
//Make all available 'c' seats unavailable
sc.find('c.available').status('unavailable');
/*
Get seats with ids 2_6, 1_7 (more on ids later on),
put them in a jQuery set and change some css
*/
sc.get(['2_6', '1_7']).node().css({
color: '#ffcfcf'
});
console.log('Seat 1_2 costs ' + sc.get('1_2').data().price + ' and is currently ' + sc.status('1_2'));
});
## Basics:
Building maps is fairly easy with jQuery Seat Charts, you can literally pass an array of strings which represents succeeding rows. Let's take a look at a theatre example:
//Seat map definition
[
'aaaaaa__DDDDD',
'aaaaaa__aaaaa',
'aaaaaa__aaaaa',
'bbbbbb__bbbbb',
'bbbbbb__bbbbb',
'bbbbbb__bbbbb',
'ccccccccccccc'
]
Each single character represents a different type of seat and you have a freedom of choosing anyone but underscore **_**. Underscore is used to indicate that there shouldn't be any seat at a certain place. In our example I chose **a** seats to be the closest to the screen, **D** meant for disabled and **b** and **c** as just plain seats. I also built a corridor in the middle of our theatre, so people can conviniently reach their seats.
Your chosen characters can carry a hash of data which is a great way to pass crucial seat details such as price or a description that you want to show on hover.
seats: {
a: {
price : 24.55,
description : 'Fair priced seat!'
}
}
Once you build your map and define seats, you can start implementing the booking magic.
## Booking Magic
JSC combines keyboard and mouse events to offer a unified API. There're three types of events which JSC can produce:
* **click**: click or spacebar
* **focus**: mouse or arrows
* **blur**: mouse or arrows
All three events have their default handlers but you're more than likely to overwrite at least one of them. JSC flexible API let you choose where you want to specify your handlers. You can define global click handlers like in the *Basic setup* example at the very beginning or you can implement separate handlers for each *character*:
a: {
click : function () {
//This will only be applied to a seats
},
price : 34.99,
category : 'VIP Seats'
}
Each event handler is fired in *seat* context which gives you an easy access (using *this* variable) to its properties, DOM node and data which you may have specified during the setup:
click: function () {
if (this.status() == 'available') {
//seat's available and can be taken!
//let's retrieve the data, so we can add the seat to our cart
var price = this.data().price,
category = this.data().category;
//jQuery element access example
this.node().css({
'font-size' : '25px'
});
//return new seat status
return 'selected';
}
//…
}
**Please note**: event handler should return new status of a seat depending on what happended. If user clicks on a seat and the seat's *available*, *selected* status should be returned. If user clicks on a *selected* seat, it most likely should become *available* again. Full status reference:
* **available**: seat which can be taken
* **unavailable**: seat which cannot be taken
* **selected**: seat which has been taken by current user
Since JSC also works with *focus/blur* events, it features a special status called *focused* which actually doesn't apply to seat status but rather to the way it's displayed. If you use *.status* method on a focused seat, you will get its real status. To get an idea of this, please take a look at how events are handled by default:
click : function() {
if (this.status() == 'available') {
return 'selected';
} else if (this.status() == 'selected') {
return 'available';
} else {
/*
If we don't want to change the status (i.e. seat's unavailable) we ought to return this.style(). this.style() is a reference to seat's special status which means that it can be focused as well. You shouldn't return this.status() here
*/
return this.style();
}
},
focus : function() {
if (this.status() == 'available') {
//if seat's available, it can be focused
return 'focused';
} else {
//otherwise nothing changes
return this.style();
}
},
blur : function() {
//The only place where you should return actual seat status
return this.status();
},
***
Your site's popular and people fight for your tickets? Don't forget to update your map with new bookings live!
//sc will contain a reference to the map
var sc = $('#sc-container').seatCharts({
//...
});
setInterval(function() {
$.ajax({
type : 'get',
url : '/bookings/get/100',
dataType : 'json',
success : function(response) {
//iterate through all bookings for our event
$.each(response.bookings, function(index, booking) {
//find seat by id and set its status to unavailable
sc.status(booking.seat_id, 'unavailable');
});
}
});
}, 10000); //every 10 seconds
## Options
Required params are marked with *
### animate
Bool, enables animated status switches.
**Please note**: *animate* uses *switchClass* method of [jQuery UI](http://jqueryui.com/), so if you want to use *animate*, you need to include jQuery UI in the page.
### blur
Blur handler. Fired when seat loses focus due to mouse move or arrow hit. You most likely don't want to overwrite this one.
//default handler
blur : function() {
return this.status();
},
### click
Click handler. Fired when user clicks on a seat or hits spacebar on a focused seat. You're most likely to overwrite this one based off this example:
click : function() {
if (this.status() == 'available') {
//do some custom stuff
console.log(this.data());
return 'selected';
} else if (this.status() == 'selected') {
//do some custom stuff
return 'available';
} else {
//i.e. alert that the seat's not available
return this.style();
}
},
### focus
Focus handler. Fired when seat receives focus. You most likely don't want to overwrite this one.
//default handler
focus : function() {
if (this.status() == 'available') {
return 'focused';
} else {
return this.style();
}
},
### legend
JSC is able to create an UL element with a map legend based on your seat types and custom CSS. If you want JSC to generate a legend for you, you will just need to pass some basic information:
##### node
jQuery reference to a DIV element where legend should be rendered. If it's missing, JSC will create a DIV container itself.
node : $('#my-legend-container')
##### items
An array of legend item details. Each array element should be a three-element array: [ *character, status, description* ]
legend : {
node : $('#my-legend-container'),
items : [
[ v, 'available', 'VIP seats!' ],
[ e, 'available', 'Economy seats'],
[ e, 'unavailable', 'Unavailable economy seats' ]
]
}
### map*
An array of strings that represents your map:
[
'aaa___aaa',
'aaaa_aaaa',
'aaaa_aaaa'
]
Underscore is used as a spacer between seats.
**Please note**: number of columns must be equal in each row.
**New**: You can now override label and ID per character. This is optional and can be applied to any number of seats:
[
'a[ID,LABEL]a[ID2,LABEL2]a___a[JUST_ID1]aa',
'aaaa_aaaa[,JUST_LABEL1]',
'aaaa_aaaa'
]
ID and/or label should be specified after the letter and enclosed in square brackets. ID should go first, optionally followed by custom label. If you just want to specify label without overriding ID, leave ID empty: a[,Just Label]
ID may contain letters, numbers and underscores. Label can contain the same groups of characters as well as spaces.
### naming
You can specify your own column and row labels as well as functions for generating seat ids and labels.
**columns**
An array of column names, *columns.length* must equal the actual number of columns:
columns: ['A', 'B', 'C', 'D', 'E']
If you don't define your own columns, succeeding numbers starting from 1 will be used.
**getId**
Callback which may accept the following parameters: *character*, *row*, *column*, where *row* and *column* are names either specified by you using *columns* and *rows* arrays or by default JSC settings. This function should return an id based off passed arguments. Default getId function:
getId : function(character, row, column) {
return row + '_' + column;
}
Returned id is not only used as an internal identifier but also as a DOM id.
**getLabel**
Callback which may accept the following parameters: *character*, *row*, *column*, where *row* and *column* are names either specified by you using *columns* and *rows* arrays or by default JSC settings. This function should return a seat label based off passed arguments. Default getLabel function:
getLabel : function (character, row, column) {
return column;
}
Labels will be displayed over seats, so if you don't want any labels, just return an empty string.
Sometimes it can be really hard to generate labels you want with getLabel, so now it's possible to specify custom labels per each seat. Please take a look at the map section.
**left**
Bool, defaults to true. If true, JSC will display an additional column on the left of the map with row names as specified by you using *rows* array or by default JSC settings
**rows**
An array of row names, *rows* length must equal the actual number of rows:
rows: ['I', 'II', 'III', 'IV', 'V']
If you don't define your own rows, succeeding numbers starting from 1 will be used.
**top**
Bool, defaults to true. If true, JSC will display an additional row on the top of the map with column names as specified by you using *columns* array or by default JSC settings
### seats
A hash of seat options, seat *characters* should be used as keys. You can pass the following params:
**blur**
Blur event which should be applied only to seats of a particular *character*.
**classes**
Custom CSS classes which should be applied to seats. Either an array or a string, JSC doesn't care:
classes : 'seat-red seat-big'
//equals
classes : ['seat-red', 'seat-big']
**click**
Custom click handler.
**focus**
Custom focus handler.
## Selectors
JSC offers you two flexible selector methods that are chainable and return *set* of seats:
### .get( ids )
You can pass either one id or an array of ids:
sc.get('2_3'); //get 2_3 seat
sc.get(['2_3', '2_4']); //get 2_3 and 2_4 seats
### .find( mixed )
Find method lets you search using *character*, seat status, combination of both (separated with a dot) or a regexp:
sc.find('a'); //find all a seats
sc.find('unavailable'); //find all unavailable seats
sc.find('a.available'); //find all available a seats
sc.find(/^1_[0-9]+/); //find all seats in the first row
#### .get and .find chained together:
sc.get(['1_2', '1_3', '1_4']).find('available'); //find available seats within specified seat ids
Both methods return either one seat or a set of seats which share similiar methods:
## Set Methods
### .status( ids, status )
Update status for a seat set with given ids. *ids* variable may contain a single id or a an array of ids.
sc.status('2_15', 'unvailable'); //set status for one seat
sc.status(['2_15', '2_10'], 'unvailable'); //set status for two seats
### .status( status )
Update status for all seats in the current set.
sc.find('unavailable').status('available'); //make all unvailable seats available
### .node( )
Returns a jQuery set of seat node references.
sc.find('unavailable').node().fadeOut('fast'); //make all unavailable seats disappear
### .each( callback )
Iterates through a seat set, callback will be fired in the context of each element. Callback may accept seat id as an argument.
sc.find('a.unavailable').each(function(seatId) {
console.log(this.data()); //display seat data
});
You can break the loop returning *false*.
## Seat Methods
### .status( [status] )
If *status* argument is set, it will be used as a new seat status, otherwise current status will be returned.
### .node( )
Returns a reference to jQuery element.
### .data( )
Returns a reference to seat data.
### .char( )
Returns seat *character*.
## Styling
JSC uses a few CSS classes that are pretty self explanatory:
### .seatCharts-container
DIV container where seat chart's rendered.
### .seatCharts-row
DIV element which serves as a row. You're most likely to edit its height.
### .seatCharts-cell
This class is applied to both seats and spacers ( _ ).
### .seatCharts-seat
Applied to all seats regardless of character.
### .seatCharts-space
Applied to spacers.
### .seatCharts-seat.selected
*Selected* seats.
### .seatCharts-seat.focused
*Focused* seats.
### .seatCharts-seat.available
*Available* seats.
### .seatCharts-seat.unavailable
*Unavailable* seats.
**Please note:** if you need each of your seat type (indicated by character) look differently, this is the easiest way:
CSS:
.seatCharts-seat.selected.vip {
background-color: #ff4fff;
}
.seatCharts-seat.focused.vip {
background-color: #ccffcc;
}
//…
.seatCharts-seat.selected.economy {
background-color: #000fff;
}
//…
JavaScript:
var sc = $.seatCharts({
seats: {
v: {
classes: 'vip',
price : 300
},
e: {
classes: 'economy',
price : 50
}
}
//…
});
### .seatCharts-legendList
UL element which holds the legend.
### .seatCharts-legendItem
LI element of the legend.
## FAQ
#### What licence is jQuery Seat Charts released under?
jQuery Seat Charts is released under [MIT license](http://choosealicense.com/licenses/mit/).
#### How is JSC accessible?
JSC implements [WAI-ARIA](www.w3.org/WAI/intro/aria) standard meaning that people using solely keyboards will share the same experience as mouse-users. You can easily check it yourself navigating with arrows and hitting spacebar instead of mouse click.

View File

@@ -0,0 +1,7 @@
# TODO
* Extend the .get selector so it's possible to pass each seat id as a separate argument. Let's keep the array method for backward compatibility,
* Add some way for rebuilding the map
* Aria tests
* Seat SeatSet functions instead of plain Objects

View File

@@ -0,0 +1,68 @@
div.seatCharts-container {
/*min-width: 700px;*/
}
div.seatCharts-cell {
height: 16px;
width: 16px;
margin: 3px;
float: left;
text-align: center;
outline: none;
font-size: 13px;
line-height:16px;
color: blue;
}
div.seatCharts-seat {
background-color: green;
color: white;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
cursor: default;
}
div.seatCharts-seat:focus {
border: none;
}
/*
.seatCharts-seat:focus {
outline: none;
}
*/
div.seatCharts-space {
background-color: white;
}
div.seatCharts-row {
height: 50px;
}
div.seatCharts-row:after {
clear: both;
}
div.seatCharts-seat.selected {
background-color: aqua;
}
div.seatCharts-seat.focused {
background-color: #6db131;
}
div.seatCharts-seat.available {
background-color: green;
}
div.seatCharts-seat.unavailable {
background-color: red;
cursor: not-allowed;
}
ul.seatCharts-legendList {
list-style: none;
}
li.seatCharts-legendItem {
margin-top: 10px;
line-height: 2;
}

View File

@@ -0,0 +1,627 @@
/*!
* 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);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,509 @@
(function ($) {
module('general');
//Creates a very simple map and returns map's container.
function simpleMapSetup(params) {
var $fixture = $('#qunit-fixture'),
$div = $('<div id="seat-map">');
$fixture.append($div);
$div.seatCharts(
$.extend(true, {}, {
map: [
'aa_aa',
'bbbbb',
'bbbbb'
],
seats: {
a: {
classes : 'a1-seat-class a2-seat-class'
},
b: {
classes : ['b1-seat-class', 'b2-seat-class']
}
}
}, params)
);
return $div;
}
function Counter() {
this.click = 0;
this.focus = 0;
this.blur = 0;
this.reset = function () {
this.click = this.focus = this.blur = 0;
};
}
test('Testing general structure of a simple map.', function () {
expect(5);
var $seatCharts = simpleMapSetup(),
$space = $seatCharts.find('.seatCharts-row:eq(1) .seatCharts-cell:eq(3)');
equal($seatCharts.find('.seatCharts-row').length, 4, 'Number of rows.');
ok($space.hasClass('seatCharts-space') && !$space.hasClass('seatCharts-seat'), 'There should be a spacer cell in the first row.')
equal($seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat').length, 4, 'Number of columns in row 1.');
for (var i = 2; i <= 3; i += 1) {
equal($seatCharts.find('.seatCharts-row:eq('+(i)+') .seatCharts-seat').length, 5, 'Number of columns in row '+i+'.');
}
});
test('Testing seat classes', function () {
expect(6);
var $seatCharts = simpleMapSetup(),
$aSeat = $seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(0)'),
$bSeat = $seatCharts.find('.seatCharts-row:eq(2) .seatCharts-seat:eq(3)');
ok($aSeat.hasClass('a1-seat-class'), 'Seat a has its a1-seat-class assigned using a string.');
ok($aSeat.hasClass('a2-seat-class'), 'Seat a has its a2-seat-class assigned using a string.');
ok($bSeat.hasClass('b1-seat-class'), 'Seat b has its b1-seat-class assigned using an array.');
ok($bSeat.hasClass('b2-seat-class'), 'Seat b has its b2-seat-class assigned using an array.');
ok(!($aSeat.hasClass('b1-seat-class') || $aSeat.hasClass('b2-seat-class')), 'Seat a does not have any b seat classes.');
ok(!($bSeat.hasClass('a1-seat-class') || $bSeat.hasClass('a2-seat-class')), 'Seat b does not have any a seat classes.');
});
test('Testing default column & row labels', function () {
expect(9);
var $seatCharts = simpleMapSetup();
equal($seatCharts.find('.seatCharts-row:eq(0) .seatCharts-cell:eq(0)').text(), '', 'Top leftmost cell should be empty.');
for (var i = 1; i <= 5; i += 1) {
equal($seatCharts.find('.seatCharts-row:eq(0) .seatCharts-cell:eq('+i+')').text(), i, 'Column header '+i+' has correct label.');
}
for (var i = 1; i <= 3; i += 1) {
equal($seatCharts.find('.seatCharts-row:eq('+i+') .seatCharts-cell:eq(0)').text(), i, 'Row header '+i+' has correct label.');
}
});
test('Testing custom column & row labels', function () {
expect(3);
var $seatCharts = simpleMapSetup({
naming : {
columns : ['I', 'II', 'III', 'IV', 'V'],
rows : ['a', 'b', 'c']
}
});
equal($seatCharts.find('.seatCharts-row:eq(0) .seatCharts-cell:eq(0)').text(), '', 'Top leftmost cell should be empty.');
equal($seatCharts.find('.seatCharts-row:eq(0) .seatCharts-cell:eq(3)').text(), 'III', '3rd column header has correct label.');
equal($seatCharts.find('.seatCharts-row:eq(2) .seatCharts-cell:eq(0)').text(), 'b', '2nd row header has correct label.');
});
test('Testing default seat labels and IDs', function () {
expect(4);
var $seatCharts = simpleMapSetup();
equal($seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(0)').text(), '1', 'First seat in the first row label.');
equal($seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(2)').text(), '4', 'Third seat in the first row label.');
equal($seatCharts.find('#3_5').length, 1, 'Seat with id 3_5 exists.');
equal($seatCharts.find('#3_6').length, 0, 'And it is the last seat id.');
});
test('Testing custom seat labels and IDs', function () {
expect(4);
var getIdExecutions = 0,
getLabelExecutions = 0,
$seatCharts = simpleMapSetup({
naming : {
getId : function (character, row, column) {
getIdExecutions += 1
//return all arguments separated with -
return [].slice.call(arguments).join('-');
},
getLabel : function (character, row, column) {
getLabelExecutions += 1;
//return all arguments separated with +
return [].slice.call(arguments).join('+');
}
}
});
equal(getIdExecutions, 14, 'getId has been called for each seat.');
equal(getLabelExecutions, 14, 'getLabel has been called for each seat.');
equal($seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(2)').text(), 'a+1+4', 'Correct label assigned.');
equal($seatCharts.find('.seatCharts-row:eq(3) .seatCharts-seat:eq(4)').attr('id'), 'b-3-5', 'Correct id assigned.');
});
test('Testing overriding labels and IDs', function () {
expect(10);
var $seatCharts = simpleMapSetup({
map: [
'a[1_A1,A1]a[1_A2,A2]_aa',
'bbbbb',
'bb[3_B2]bbb[,B5]'
]
}),
//a[1_A1,A1]
$seat1 = $seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(0)'),
//a[1_A2,A2]
$seat2 = $seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(1)'),
//a
$seat3 = $seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(2)'),
//b[3_B2]
$seat4 = $seatCharts.find('.seatCharts-row:eq(3) .seatCharts-seat:eq(1)'),
//b[,B5]
$seat5 = $seatCharts.find('.seatCharts-row:eq(3) .seatCharts-seat:eq(4)');
equal($seat1.text(), 'A1', 'Seat 1 has correct label assigned.');
equal($seat1.attr('id'), '1_A1', 'Seat 1 has correct id assigned.');
equal($seat2.text(), 'A2', 'Seat 2 has correct label assigned.');
equal($seat2.attr('id'), '1_A2', 'Seat 2 has correct id assigned.');
equal($seat3.text(), '4', 'Seat 3 has correct label assigned.');
equal($seat3.attr('id'), '1_4', 'Seat 3 has correct id assigned.');
equal($seat4.text(), '2', 'Seat 4 has correct label assigned.');
equal($seat4.attr('id'), '3_B2', 'Seat 4 has correct id assigned.');
equal($seat5.text(), 'B5', 'Seat 5 has correct label assigned.');
equal($seat5.attr('id'), '3_5', 'Seat 5 has correct id assigned.');
});
test('Testing overriding+custom labels and IDs', function () {
expect(12);
var getIdExecutions = 0,
getLabelExecutions = 0,
$seatCharts = simpleMapSetup({
naming : {
getId : function (character, row, column) {
getIdExecutions += 1
//return all arguments separated with -
return [].slice.call(arguments).join('-');
},
getLabel : function (character, row, column) {
getLabelExecutions += 1;
//return all arguments separated with +
return [].slice.call(arguments).join('+');
}
},
map: [
'a[1_A1,A1]a[1_A2,A2]_aa',
'bbbbb',
'bb[3_B2]bbb[,B5]'
]
}),
//a[1_A1,A1]
$seat1 = $seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(0)'),
//a[1_A2,A2]
$seat2 = $seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(1)'),
//a
$seat3 = $seatCharts.find('.seatCharts-row:eq(1) .seatCharts-seat:eq(2)'),
//b[3_B2]
$seat4 = $seatCharts.find('.seatCharts-row:eq(3) .seatCharts-seat:eq(1)'),
//b[,B5]
$seat5 = $seatCharts.find('.seatCharts-row:eq(3) .seatCharts-seat:eq(4)');
equal(getIdExecutions, 11, 'getId has been called for each seat.');
equal(getLabelExecutions, 11, 'getLabel has been called for each seat.');
equal($seat1.text(), 'A1', 'Seat 1 has correct label assigned.');
equal($seat1.attr('id'), '1_A1', 'Seat 1 has correct id assigned.');
equal($seat2.text(), 'A2', 'Seat 2 has correct label assigned.');
equal($seat2.attr('id'), '1_A2', 'Seat 2 has correct id assigned.');
equal($seat3.text(), 'a+1+4', 'Seat 3 has correct label assigned.');
equal($seat3.attr('id'), 'a-1-4', 'Seat 3 has correct id assigned.');
equal($seat4.text(), 'b+3+2', 'Seat 4 has correct label assigned.');
equal($seat4.attr('id'), '3_B2', 'Seat 4 has correct id assigned.');
equal($seat5.text(), 'B5', 'Seat 5 has correct label assigned.');
equal($seat5.attr('id'), 'b-3-5', 'Seat 5 has correct id assigned.');
});
test('Testing disabled left & top containers for labels', function () {
var $seatCharts = simpleMapSetup({
naming : {
top: false,
left: false
}
});
ok($seatCharts.find('.seatCharts-row:eq(0) .seatCharts-cell:eq(0)').hasClass('seatCharts-seat'), 'Top leftmost cell should contain a seat.');
})
test('Testing legend with container specified', function () {
expect(4);
var $fixture = $('#qunit-fixture'),
$legend = $('<div>').appendTo($fixture),
$seatCharts = simpleMapSetup({
legend : {
node : $legend,
items : [
['a', 'available', 'A seat when available'],
['b', 'available', 'B seat when available'],
['a', 'unavailable', 'A seat when unavailable'],
['b', 'unavailable', 'B seat when unavailable']
]
}
}),
$item,
$seat;
ok($legend.hasClass('seatCharts-legend'), 'Legend class has been assigned to the container');
equal($legend.find('ul.seatCharts-legendList li.seatCharts-legendItem').length, 4, 'There is a list of 4 legend items.');
$item = $legend.find('.seatCharts-legendItem:eq(0)');
$seat = $item.find('div:first');
ok($seat.hasClass('seatCharts-seat') && $seat.hasClass('seatCharts-cell') && $seat.hasClass('available') && $seat.hasClass('a1-seat-class') && $seat.hasClass('a2-seat-class'), 'The first legend item has correct classes assigned.');
equal($item.find('.seatCharts-legendDescription').text(), 'A seat when available', 'The first item has correct label assigned.');
});
test('Testing legend without container specified', function () {
expect(4);
var $fixture = $('#qunit-fixture'),
$seatCharts = simpleMapSetup({
legend : {
items : [
['a', 'available', 'A seat when available'],
['b', 'available', 'B seat when available'],
['a', 'unavailable', 'A seat when unavailable'],
['b', 'unavailable', 'B seat when unavailable']
]
}
}),
$legend,
$item,
$seat;
equal($('div.seatCharts-legend').length, 1, 'Legend div has been created.');
$legend = $('div.seatCharts-legend:eq(0)');
equal($legend.find('ul.seatCharts-legendList li.seatCharts-legendItem').length, 4, 'There is a list of 4 legend items.');
$item = $legend.find('.seatCharts-legendItem:eq(0)');
$seat = $item.find('div:first');
ok($seat.hasClass('seatCharts-seat') && $seat.hasClass('seatCharts-cell') && $seat.hasClass('available') && $seat.hasClass('a1-seat-class') && $seat.hasClass('a2-seat-class'), 'The first legend item has correct classes assigned.');
equal($item.find('.seatCharts-legendDescription').text(), 'A seat when available', 'The first item has correct label assigned.');
});
test('Testing map-level callbacks', function () {
var $seatCharts = simpleMapSetup({
click : function () {
executions.click += 1;
if (this.status() == 'available') {
return 'selected';
} else if (this.status() == 'selected') {
return 'available';
} else {
return this.style();
}
},
focus : function() {
executions.focus += 1;
if (this.status() == 'available') {
return 'focused';
} else {
return this.style();
}
},
blur : function() {
executions.blur += 1;
return this.status();
}
}),
seatCharts = $seatCharts.seatCharts(),
//simple counter object
executions = new Counter,
clickEvent = $.Event('click'),
mouseenterEvent = $.Event('mouseenter'),
mouseleaveEvent = $.Event('mouseleave'),
focusEvent = $.Event('focus'),
keyEvent = $.Event('keydown');
seatCharts.get('2_3').status('unavailable');
seatCharts.get('2_1').node().trigger(mouseenterEvent);
seatCharts.get('2_1').node().trigger(mouseleaveEvent);
seatCharts.get('2_2').node().trigger(mouseenterEvent);
seatCharts.get('2_2').node().trigger(mouseleaveEvent);
seatCharts.get('2_3').node().trigger(mouseenterEvent);
propEqual(executions, {
click : 0,
focus : 3,
blur : 4,
reset : function () {}
}, 'Blur and focus are correctly triggered.');
//start over
executions.reset();
seatCharts.get('3_1').node().trigger(mouseenterEvent);
//arrow down
keyEvent.which = 38;
seatCharts.get('3_1').node().trigger(keyEvent);
//spacebar
keyEvent.which = 32;
seatCharts.get('2_1').node().trigger(keyEvent);
propEqual(executions, {
click : 1,
focus : 2,
blur : 3,
reset : function () {}
}, 'Blur, focus and click are correctly triggered.');
});
test('Testing seat-level callbacks', function () {
var $seatCharts = simpleMapSetup({
seats : {
a : {
click : function () {
executionsA.click += 1;
if (this.status() == 'available') {
return 'selected';
} else if (this.status() == 'selected') {
return 'available';
} else {
return this.style();
}
},
focus : function() {
executionsA.focus += 1;
if (this.status() == 'available') {
return 'focused';
} else {
return this.style();
}
},
blur : function() {
executionsA.blur += 1;
return this.status();
}
},
b : {
click : function () {
executionsB.click += 1;
if (this.status() == 'available') {
return 'selected';
} else if (this.status() == 'selected') {
return 'available';
} else {
return this.style();
}
},
focus : function() {
executionsB.focus += 1;
if (this.status() == 'available') {
return 'focused';
} else {
return this.style();
}
},
blur : function() {
executionsB.blur += 1;
return this.status();
}
}
}
}),
seatCharts = $seatCharts.seatCharts(),
//each seat type has its own callbacks and hence different counters
executionsA = new Counter,
executionsB = new Counter,
clickEvent = $.Event('click'),
mouseenterEvent = $.Event('mouseenter'),
mouseleaveEvent = $.Event('mouseleave'),
focusEvent = $.Event('focus'),
keyEvent = $.Event('keydown');
seatCharts.get('2_3').status('unavailable');
seatCharts.get('2_1').node().trigger(mouseenterEvent);
seatCharts.get('2_1').node().trigger(mouseleaveEvent);
seatCharts.get('2_2').node().trigger(mouseenterEvent);
seatCharts.get('2_2').node().trigger(mouseleaveEvent);
seatCharts.get('2_3').node().trigger(mouseenterEvent);
propEqual(executionsB, {
click : 0,
focus : 3,
blur : 4,
reset : function () {}
}, 'Blur, focus and click are correctly triggered for B seats.');
seatCharts.get('1_1').node().trigger(mouseenterEvent);
//arrow right
keyEvent.which = 39;
seatCharts.get('1_1').node().trigger(keyEvent);
seatCharts.get('1_2').node().trigger(keyEvent);
//spacebar
keyEvent.which = 32;
seatCharts.get('1_4').node().trigger(keyEvent);
propEqual(executionsA, {
click : 1,
focus : 3,
blur : 4,
reset : function () {}
}, 'Blur, focus and click are correctly triggered for A seats.');
});
})(jQuery);

View File

@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>JSC Test Suite</title>
<link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.14.0.css">
<link rel="stylesheet" href="../jquery.seat-charts.css">
<script src="http://code.jquery.com/qunit/qunit-1.14.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="../jquery.seat-charts.min.js"></script>
<script type="text/javascript" src="general.js"></script>
<script type="text/javascript" src="methods.js"></script>
<script type="text/javascript" src="interactions.js"></script>
<script type="text/javascript" src="multiple.js"></script>
</head>
<body>
<h1 id="qunit-header">JSC Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<div id="qunit-fixture"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
</body>
</html>

View File

@@ -0,0 +1,283 @@
(function ($) {
module('interactions');
//Creates a very simple map and returns map's container.
function interactionsMapSetup(params) {
var $fixture = $('#qunit-fixture'),
$div = $('<div id="seat-map">');
$fixture.append($div);
$div.seatCharts(
$.extend(true, {}, {
map: [
'aa_aa',
'aaaaa',
'bbbbb',
'bbbbb',
'ccccc',
'_____',
'cc_cc',
'_____',
'cc___'
],
seats: {
a: {
classes : 'a1-seat-class a2-seat-class',
price : 45,
anObject : {
aProperty: 'testing'
}
},
b: {
classes : ['b1-seat-class', 'b2-seat-class'],
price : 25,
anObject2: {
aProperty2: 23
}
}
}
}, params)
);
return $div;
}
test('Testing focus/blur with mouse', function () {
expect(8);
var $seatCharts = interactionsMapSetup(),
seatCharts = $seatCharts.seatCharts(),
seat1 = seatCharts.get('3_4'),
seat2 = seatCharts.get('4_1'),
mouseenterEvent,
focusEvent;
//focus the first seat
mouseenterEvent = $.Event('mouseenter');
seat1.node().trigger(mouseenterEvent);
equal(seat1.style(), 'focused', 'Test if focused using .style function.');
equal(seat1.node()[0], document.activeElement, 'Test if focused using document.activeElement');
equal($seatCharts.attr('aria-activedescendant'), seat1.node().attr('id'), 'Test if aria-activedescendant has been populated with the correct id.');
//move focus to some other seat
seat2.node().trigger(mouseenterEvent);
equal(seat1.style(), 'available', 'Test if previous seat lost focus using .style function.');
equal(seat2.style(), 'focused', 'Test if focused using .style function.');
equal(seat2.node()[0], document.activeElement, 'Test if focused using document.activeElement');
equal($seatCharts.attr('aria-activedescendant'), '4_1', 'Test if aria-activedescendant has been populated with the correct id.');
focusEvent = $.Event('focus');
$seatCharts.trigger(focusEvent);
equal(document.getElementById('1_1'), document.activeElement, 'First seat should be focused by default');
});
test('Testing focus/blur with keyboard', function () {
expect(14);
var $seatCharts = interactionsMapSetup(),
seatCharts = $seatCharts.seatCharts(),
focusEvent,
leftEvent,
rightEvent,
upEvent,
downEvent;
focusEvent = $.Event('focus');
$seatCharts.trigger(focusEvent);
leftEvent = $.Event('keydown');
leftEvent.which = 37;
rightEvent = $.Event('keydown');
rightEvent.which = 39;
downEvent = $.Event('keydown');
downEvent.which = 40;
upEvent = $.Event('keydown');
upEvent.which = 38;
seatCharts.get('1_1').node().trigger(rightEvent);
//right arrow
equal(document.activeElement, document.getElementById('1_2'), 'Right arrow moves focus to the right seat.');
equal($seatCharts.attr('aria-activedescendant'), seatCharts.get('1_2').node().attr('id'), 'Test if aria-activedescendant has been populated with the correct id.');
seatCharts.get('1_2').node().trigger(rightEvent);
equal(document.activeElement, document.getElementById('1_4'), 'Right arrow moves focus to the right seat skipping the empty space.');
seatCharts.get('1_4').node().trigger(rightEvent);
seatCharts.get('1_5').node().trigger(rightEvent);
equal(document.activeElement, document.getElementById('2_1'), 'Right arrow moves focus to the first seat of the next row when it reaches the end of the current row.');
seatCharts.get('5_5').node().trigger(focusEvent);
seatCharts.get('5_5').node().trigger(rightEvent);
equal(document.activeElement, document.getElementById('7_1'), 'Right arrow moves focus to the first seat skipping empty spaces.');
seatCharts.get('9_1').node().trigger(focusEvent);
seatCharts.get('9_1').node().trigger(rightEvent);
seatCharts.get('9_2').node().trigger(rightEvent);
equal(document.activeElement, document.getElementById('1_1'), 'Right arrow moves focus to the first seat skipping empty spaces and starting over when the last seat is reached.');
//left arrow
seatCharts.get('2_3').node().trigger(focusEvent);
seatCharts.get('2_3').node().trigger(leftEvent);
equal(document.activeElement, document.getElementById('2_2'), 'Left arrow moves focus to the left seat.');
seatCharts.get('2_2').node().trigger(leftEvent);
seatCharts.get('2_1').node().trigger(leftEvent);
equal(document.activeElement, document.getElementById('1_5'), 'Left arrow moves focus to the last seat of the previous row when the beginning of the current row is reached.');
seatCharts.get('9_1').node().trigger(focusEvent);
seatCharts.get('9_1').node().trigger(leftEvent);
equal(document.activeElement, document.getElementById('7_5'), 'Left arrow moves focus to the last seat of the previous row when the beginning of the current row is reached skipping empty spaces.');
seatCharts.get('1_1').node().trigger(focusEvent);
seatCharts.get('1_1').node().trigger(leftEvent);
equal(document.activeElement, document.getElementById('9_2'), 'Left arrow moves focus to the last seat when pressed on the first seat skipping empty spaces.');
//down
seatCharts.get('2_2').node().trigger(focusEvent);
seatCharts.get('2_2').node().trigger(downEvent);
equal(document.activeElement, document.getElementById('3_2'), 'Down arrow moves focus to the seat below.');
seatCharts.get('5_3').node().trigger(focusEvent);
seatCharts.get('5_3').node().trigger(downEvent);
equal(document.activeElement, document.getElementById('2_3'), 'Down arrow moves focus to the seat below skipping empty spaces.');
//up
seatCharts.get('4_4').node().trigger(focusEvent);
seatCharts.get('4_4').node().trigger(upEvent);
equal(document.activeElement, document.getElementById('3_4'), 'Up arrow moves focus to the seat above.');
$(document.activeElement).trigger(upEvent);
$(document.activeElement).trigger(upEvent);
$(document.activeElement).trigger(upEvent);
$(document.activeElement).trigger(upEvent);
equal(document.activeElement, document.getElementById('5_4'), 'Up arrow moves focus to the seat above skipping empty spaces.');
});
test('Testing default click callback with mouse', function () {
expect(7);
var $seatCharts = interactionsMapSetup(),
seatCharts = $seatCharts.seatCharts(),
clickEvent,
focusEvent;
//disable some seats
seatCharts.get(['1_4', '4_2']).status('unavailable');
clickEvent = $.Event('click');
focusEvent = $.Event('focus');
$('#5_2').trigger(clickEvent);
equal(seatCharts.find('selected').length, '1', 'Clicking on an available seat should change its status to selected.');
equal(seatCharts.get('5_2').style(), 'selected', 'Selected seat should return selected style.');
equal(seatCharts.get('5_2').status(), 'selected', 'Selected seat should return selected status.');
ok(seatCharts.get('5_2').node().hasClass('selected'), 'Selected seat should have selected class.');
$('#1_4').trigger(clickEvent);
equal(seatCharts.find('selected').length, '1', 'You can not select an unavailable seat.');
$('#5_2').trigger(clickEvent);
equal(seatCharts.find('selected').length, '0', 'Clicking on a selected seat should change its status to available.');
$('#3_3').trigger(focusEvent);
$('#3_3').trigger(clickEvent);
equal(seatCharts.get('3_3').status(), 'selected', 'Clicking on a focused seat should change its status to selected.');
});
test('Testing default click callback with keyboard', function () {
expect(7);
var $seatCharts = interactionsMapSetup(),
seatCharts = $seatCharts.seatCharts(),
spacebarEvent,
focusEvent;
//disable some seats
seatCharts.find('c').status('unavailable');
spacebarEvent = $.Event('keydown');
spacebarEvent.which = 32;
focusEvent = $.Event('focus');
$('#1_1').trigger(spacebarEvent);
equal(seatCharts.find('selected').length, '1', 'Pressing spacebar on an available seat should change its status to selected.');
equal(seatCharts.get('1_1').style(), 'selected', 'Selected seat should return selected style.');
equal(seatCharts.get('1_1').status(), 'selected', 'Selected seat should return selected status.');
ok(seatCharts.get('1_1').node().hasClass('selected'), 'Selected seat should have selected class.');
$('#7_2').trigger(spacebarEvent);
equal(seatCharts.find('selected').length, '1', 'You can not select an unavailable seat.');
$('#1_1').trigger(spacebarEvent);
equal(seatCharts.find('selected').length, '0', 'Pressing spacebar on a selected seat should change its status to available.');
$('#2_5').trigger(focusEvent);
$('#2_5').trigger(spacebarEvent);
equal(seatCharts.get('2_5').status(), 'selected', 'Pressing spacebar on a focused seat should change its status to selected.');
});
})(jQuery);

View File

@@ -0,0 +1,294 @@
(function ($) {
module('methods');
//Creates a very simple map and returns map's container.
function methodsMapSetup(params) {
var $fixture = $('#qunit-fixture'),
$div = $('<div id="seat-map">');
$fixture.append($div);
$div.seatCharts(
$.extend(true, {}, {
map: [
'aa_aa',
'aaaaa',
'bbbbb',
'bbbbb',
'ccccc',
],
seats: {
a: {
classes : 'a1-seat-class a2-seat-class',
price : 45,
anObject : {
aProperty: 'testing'
}
},
b: {
classes : ['b1-seat-class', 'b2-seat-class'],
price : 25,
anObject2: {
aProperty2: 23
}
}
}
}, params)
);
return $div;
}
test('Testing Seat methods & properties', function () {
expect(9);
var $seatCharts = methodsMapSetup(),
seatCharts = $seatCharts.seatCharts(),
seat = seatCharts.get('1_2');
equal(typeof seat.blur, 'function', '.blur method present.');
equal(typeof seat.char, 'function', '.char method present.');
equal(typeof seat.click, 'function', '.click method present.');
equal(typeof seat.data, 'function', '.data method present.');
equal(typeof seat.focus, 'function', '.focus method present.');
equal(typeof seat.node, 'function', '.node method present.');
equal(typeof seat.settings, 'object', '.settings property present.');
equal(typeof seat.status, 'function', '.status method present.');
equal(typeof seat.style, 'function', '.style method present.');
});
test('Testing Seat Set methods & properties', function () {
expect(11);
var $seatCharts = methodsMapSetup(),
seatCharts = $seatCharts.seatCharts(),
seat = seatCharts.get(['1_2', '4_3']);
equal(typeof seat.each, 'function', '.each method present.');
equal(typeof seat.find, 'function', '.find method present.');
equal(typeof seat.get, 'function', '.get method present.');
equal(typeof seat.length, 'number', '.length property present.');
equal(typeof seat.node, 'function', '.node method present.');
equal(typeof seat.push, 'function', '.push method present.');
equal(typeof seat.seatIds, 'object', '.seatIds property present.');
equal(seat.seatIds.length, 2, '.seatIds property correct.');
equal(typeof seat.seats, 'object', '.seats property present.');
equal(typeof seat.set, 'function', '.set method present.');
equal(typeof seat.status, 'function', '.status method present.');
});
test('Testing .get selector', function () {
expect(7);
var $seatCharts = methodsMapSetup(),
seatCharts = $seatCharts.seatCharts();
equal(typeof seatCharts.get('1_1'), 'object', '.get for one id should return an object.');
equal(typeof seatCharts.get('1_1').length, 'undefined', '.get for one id should return an object without length property.');
equal(typeof seatCharts.get(['1_1', '3_3']), 'object', '.get for two ids should return an object.');
equal(seatCharts.get(['1_1', '3_3', '2_4']).length, 3, '.get for three ids should have a property length with value 3.');
equal(typeof seatCharts.get('99_99'), 'undefined', '.get for invalid id should return undefined.');
equal(typeof seatCharts.get(['99_99', '2_3']), 'object', '.get for invalid and valid id and should return an object.');
equal(seatCharts.get(['99_99', '2_3']).length, 1, 'With 1 as length.');
});
test('Testing .status method', function () {
expect(10);
var $seatCharts = methodsMapSetup(),
seatCharts = $seatCharts.seatCharts(),
seat1,
seatsSet;
seat1 = seatCharts.get('2_3');
equal(seat1.status(), 'available', 'Default status is available.');
seat1.status('unavailable');
equal(seat1.status(), 'unavailable', 'Status has been changed.');
seat1.status('selected');
equal(seat1.status(), 'selected', 'Status has been changed again.');
seatsSet = seatCharts.get(['2_2', '2_4']);
seatsSet.status('selected');
equal(seatCharts.get('2_2').status(), 'selected', 'Setting status for the whole set 1.');
equal(seatCharts.get('2_4').status(), 'selected', 'Setting status for the whole set 2.');
seatCharts.status('3_2', 'unavailable');
equal(seatCharts.get('3_2').status(), 'unavailable', 'Alternative .status usage.');
seatCharts.status(['4_4', '5_2', '5_1'], 'unavailable');
equal(seatCharts.get('4_4').status(), 'unavailable', 'Alternative .status usage for the whole set 1.');
equal(seatCharts.get('5_2').status(), 'unavailable', 'Alternative .status usage for the whole set 2.');
equal(seatCharts.get('5_1').status(), 'unavailable', 'Alternative .status usage for the whole set 3.');
//Issue #8 - Very odd behaviour when using sc.status();
seatCharts.get(['1_1', '1_2']).status('unavailable');
//settings the same status twice
seatCharts.get(['1_1', '1_2']).status('unavailable');
equal(seatCharts.get('1_1').status(), 'unavailable', 'Status remains correct after setting the same status again');
});
test('Testing .find selector', function () {
expect(16);
var $seatCharts = methodsMapSetup(),
seatCharts = $seatCharts.seatCharts();
seatCharts.get(['4_1', '1_5', '2_3']).status('unavailable');
seatCharts.get(['5_2', '1_4']).status('selected');
equal(seatCharts.find('available').length, 19, 'Finding by status alone.');
equal(seatCharts.find('avble').length, 0, 'Finding by invalid status alone.');
equal(seatCharts.get(['4_1', '4_2']).find('unavailable').length, 1, 'Finding in set by status alone.');
equal(seatCharts.find('c').length, 5, 'Finding by character alone.');
equal(seatCharts.find('O').length, 0, 'Finding by invalid character.');
equal(seatCharts.get(['2_1', '3_2', '5_5', '5_3', '4_2']).find('b').length, 2, 'Finding in set by character alone.');
equal(seatCharts.get(['2_1', '3_2', '5_5', '5_3', '4_2']).find('_').length, 0, 'Finding in set by invalid character.');
equal(seatCharts.find('c.available').length, 4, 'Finding by character and status.');
equal(seatCharts.find('c.able').length, 0, 'Finding by character and invalid status.');
equal(seatCharts.find('P.availble').length, 0, 'Finding by invalid character and status.');
equal(seatCharts.get(['2_1', '3_2', '5_5', '5_3', '4_2', '2_3']).find('b.available').length, 2, 'Finding in set by character and status.');
equal(seatCharts.get(['2_1', '3_2', '5_5', '5_3', '4_2', '2_3']).find('X.available').length, 0, 'Finding in set by invalid character and status.');
equal(seatCharts.get(['2_1', '3_2', '5_5', '5_3', '4_2', '2_3']).find('c.invalid-status').length, 0, 'Finding in set by character and invalid status.');
equal(seatCharts.get(['9_12', '', '4_53']).find('b').length, 0, 'Finding in empty set.');
equal(seatCharts.find(/^1_.*/).length, 4, 'Finding first row seats using a regexp.');
equal(seatCharts.find(/^[0-9]+_3/).length, 4, 'Finding third column seats using a regexp.');
});
test('Testing .node method', function () {
expect(6);
var $seatCharts = methodsMapSetup(),
seatCharts = $seatCharts.seatCharts();
seatCharts.get(['2_2', '5_4']).status('unavailable');
ok(seatCharts.get('1_4').node() instanceof jQuery, '.node returns a jQuery object.');
equal(seatCharts.get('2_3').node().length, 1, '.node for one seat returns set with 1 element.');
equal(seatCharts.get(['2_3', '4_2']).node().length, 2, '.node for two seats returns set with 2 elements.');
equal(seatCharts.find('c.available').node().length, 4, '.node returns jQuery set with all objects matching .find query.');
equal(seatCharts.get('1_4').node()[0], $('#1_4')[0], 'The same node returned by .get and jQuery selector.');
equal(seatCharts.get(['2_2', '3_5']).node()[1], $('#3_5')[0], 'The same nodes returned by .get and jQuery selector.');
});
test('Testing .each method', function () {
expect(4);
var $seatCharts = methodsMapSetup(),
seatCharts = $seatCharts.seatCharts(),
executions1 = 0,
executions2 = 0,
executions3 = 0;
seatCharts.get(['5_2', '5_1']).status('selected');
seatCharts.find('c.available').each(function () {
executions1 += 1;
});
equal(executions1, 3, '.each callback should be called for each element of the set.');
seatCharts.find('c').each(function () {
executions2 += 1;
return false; //break
});
equal(executions2, 1, 'Returning false should break .each loop.');
seatCharts.find('Z').each(function() {
executions3 += 1;
});
equal(executions3, 0, '.each should not be called when the set is empty.');
seatCharts.find('a').each(function () {
equal(typeof this.data, 'function', 'Seat is used as the context.');
return false;
});
});
test('Testing .data method', function () {
expect(4);
var $seatCharts = methodsMapSetup(),
seatCharts = $seatCharts.seatCharts();
propEqual(seatCharts.get('1_2').data(), {
classes : 'a1-seat-class a2-seat-class',
price : 45,
anObject : {
aProperty: 'testing'
}
}, 'a seat has correct properties.');
propEqual(seatCharts.get('3_4').data(), {
classes : ['b1-seat-class', 'b2-seat-class'],
price : 25,
anObject2: {
aProperty2: 23
}
}, 'b seat has correct properties.');
propEqual(seatCharts.get('5_2').data(), {}, 'c seat has correct properties.');
seatCharts.get('1_2').data().price = '75';
equal(seatCharts.get('1_1').data().price, '75', 'All seat of the same character share the reference to the same object');
});
test('Testing .char method', function () {
var $seatCharts = methodsMapSetup(),
seatCharts = $seatCharts.seatCharts();
equal(seatCharts.get('5_5').char(), 'c', 'Correct character returned 1.');
equal(seatCharts.get('2_3').char(), 'a', 'Correct character returned 2.');
equal(seatCharts.get('4_4').char(), 'b', 'Correct character returned 3.');
});
})(jQuery);

View File

@@ -0,0 +1,57 @@
(function ($) {
module('multiple');
//testing multiple maps on page to ensure there are no conflicts
function multipleMapSetup(params) {
multipleMapSetup.counter = multipleMapSetup.counter || 1;
var $fixture = $('#qunit-fixture'),
$div = $('<div id="seat-map-'+(multipleMapSetup.counter++)+'">');
$fixture.append($div);
$div.seatCharts(
$.extend(true, {}, {
map: [
'aa_aa',
'bbbbb',
'bbbbb'
],
seats: {
a: {
classes : 'a1-seat-class a2-seat-class'
},
b: {
classes : ['b1-seat-class', 'b2-seat-class']
}
}
}, params)
);
return $div;
}
test('Testing selectors for multi maps', function () {
var $seatCharts1 = multipleMapSetup(),
seatCharts1 = $seatCharts1.seatCharts(),
$seatCharts2 = multipleMapSetup({
map: [
'eeeeeeeee',
'baaabaaaa',
'bbbbb____'
],
}),
seatCharts2 = $seatCharts2.seatCharts();
seatCharts1.find('a.available').status('unavailable');
equal(seatCharts1.find('unavailable').length, 4, 'Status has been changed correctly');
equal(seatCharts2.find('unavailable').length, 0, 'Status has not been changed for the other map.');
});
})(jQuery);