gridiron
This commit is contained in:
commit
0b97eaffad
111
citiestojson.php
Normal file
111
citiestojson.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
$sortColumn = @$_GET['sort_column'];
|
||||
$sortDirection = @$_GET['sort_dir'];
|
||||
$limit = @$_GET['limit'];
|
||||
$offset = @$_GET['offset'];
|
||||
$filters = @$_GET['filter'];
|
||||
$searchQuery = @$_GET['query'];
|
||||
|
||||
if (!ctype_digit($limit) || !ctype_digit($offset) || $limit < 0 || $offset < 0) {
|
||||
header('HTTP/1.1 400 Bad Request');
|
||||
exit;
|
||||
}
|
||||
|
||||
$conn = mysql_connect('localhost', 'geodata', 'geodata');
|
||||
mysql_select_db('geodata', $conn);
|
||||
|
||||
$query = '
|
||||
SELECT SQL_CALC_FOUND_ROWS
|
||||
c.City,
|
||||
r.Region,
|
||||
co.Country,
|
||||
c.Latitude,
|
||||
c.Longitude,
|
||||
co.Population
|
||||
FROM cities c
|
||||
INNER JOIN countries co
|
||||
ON co.countryId = c.CountryID
|
||||
INNER JOIN regions r
|
||||
ON r.RegionID = c.RegionID
|
||||
';
|
||||
|
||||
$whereClauses = array();
|
||||
|
||||
if (!empty($searchQuery)) {
|
||||
$whereClauses[] = 'c.City LIKE \'%' . mysql_real_escape_string($searchQuery, $conn) . '%\'';
|
||||
}
|
||||
|
||||
if (!empty($filters)) {
|
||||
foreach ($filters as $column => $searchQuery) {
|
||||
switch ($column) {
|
||||
case 'city':
|
||||
$whereClauses[] = 'c.City LIKE \'%' . mysql_real_escape_string($searchQuery, $conn) . '%\'';
|
||||
break;
|
||||
case 'country':
|
||||
$whereClauses[] = 'co.Country LIKE \'%' . mysql_real_escape_string($searchQuery, $conn) . '%\'';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($whereClauses)) {
|
||||
$query .= 'WHERE ' . implode("\n\t\tAND ", $whereClauses) . "\n\t\t";
|
||||
}
|
||||
|
||||
if (!empty($sortColumn)) {
|
||||
switch ($sortColumn) {
|
||||
case 'region':
|
||||
$query .= 'ORDER BY r.Region';
|
||||
break;
|
||||
case 'country':
|
||||
$query .= 'ORDER BY co.Country';
|
||||
break;
|
||||
case 'city':
|
||||
case 'latitude':
|
||||
case 'longitude':
|
||||
$query .= 'ORDER BY c.' . ucfirst($sortColumn);
|
||||
break;
|
||||
default:
|
||||
$fail = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isset($fail) && $sortDirection === 'desc') {
|
||||
$query .= ' DESC';
|
||||
}
|
||||
}
|
||||
|
||||
$query .= '
|
||||
LIMIT ' . $limit . ' OFFSET ' . $offset;
|
||||
|
||||
$result = mysql_query($query, $conn);
|
||||
if (!$result) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
echo 'The database exploded!';
|
||||
exit;
|
||||
}
|
||||
|
||||
$json = new stdClass();
|
||||
$json->records = array();
|
||||
$json->offset = (int)$offset;
|
||||
while ($row = mysql_fetch_assoc($result)) {
|
||||
$obj = new stdClass();
|
||||
|
||||
$obj->city = $row['City'];
|
||||
$obj->latitude = (float)$row['Latitude'];
|
||||
$obj->longitude = (float)$row['Longitude'];
|
||||
$obj->region = $row['Region'];
|
||||
$obj->country = $row['Country'];
|
||||
$obj->countryPopulation = (int)$row['Population'];
|
||||
|
||||
$json->records[] = $obj;
|
||||
}
|
||||
|
||||
$json->totalRecordCount = (int)mysql_result(mysql_query('SELECT FOUND_ROWS()', $conn), 0);
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($json);
|
||||
exit;
|
||||
|
||||
?>
|
116
geodata.sql
Normal file
116
geodata.sql
Normal file
File diff suppressed because one or more lines are too long
35
index.html
Normal file
35
index.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
|
||||
<head>
|
||||
<title>GridIron: jQuery grid plugin</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>GridIron</h1>
|
||||
|
||||
<p>
|
||||
GridIron is a <a href="http://jquery.com/">jQuery</a> plugin <a href="http://tommymontgomery.com/">I</a>
|
||||
wrote because I hated all the other ones. There was always something it couldn’t do, and
|
||||
it was just difficult enough where it would take weeks of hacking source code, reading documentation
|
||||
and constant profanity to implement it myself.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
So I figured, why not write my own? So I did. This (so far) is the result of two weekends’ worth
|
||||
of work.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Eventually there will be documentation and stuff here as soon as I write it. But until then:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="test.html">Demo</a></li>
|
||||
<li><a href="http://tommymontgomery.com/contact">Comment</a></li>
|
||||
<li><a href="http://websvn.tommymontgomery.com/filedetails.php?repname=main&path=%2Fgridiron%2Fjquery.gridiron.js">Source</a></li>
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
126
jquery.gridiron.css
Normal file
126
jquery.gridiron.css
Normal file
@ -0,0 +1,126 @@
|
||||
body {
|
||||
font-family: Calibri;
|
||||
font-size: 8pt;
|
||||
margin: 10px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.gridiron-row {
|
||||
border-bottom: 1px solid #CCCCCC;
|
||||
border-top: 1px solid transparent;
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.gridiron-grid input {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.gridiron-cell {
|
||||
display: inline-block;
|
||||
border-left: 1px solid #DDDDDD;
|
||||
padding: 0 2px;
|
||||
border-right: none !important;
|
||||
border-top: none !important;
|
||||
border-bottom: none !important;
|
||||
height: 100%;
|
||||
line-height: 24px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
.gridiron-first {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
.gridiron-header, .gridiron-footer {
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
border-top: none !important;
|
||||
}
|
||||
.gridiron-header .gridiron-cell {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.gridiron-data-container {
|
||||
overflow: auto;
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
position: relative;
|
||||
}
|
||||
.gridiron-row-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gridiron-sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gridiron-sort-indicator {
|
||||
position: relative;
|
||||
top: 15%;
|
||||
display: inline-block !important;
|
||||
}
|
||||
.gridiron-footer .ui-icon {
|
||||
display: inline-block !important;
|
||||
}
|
||||
.gridiron-pagination-previous, .gridiron-pagination-next {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.gridiron-record-display {
|
||||
float: right;
|
||||
margin: 3px 5px 0 0;
|
||||
}
|
||||
.gridiron-pagination-page-display {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
}
|
||||
.gridiron-footer {
|
||||
font-size: 90%;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
.gridiron-footer input {
|
||||
width: 2em;
|
||||
height: 1em;
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
||||
.gridiron-loading-indicator {
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
.gridiron-loading-indicator .gridiron-spinner {
|
||||
background-image: url(loading.gif);
|
||||
display: block;
|
||||
margin: auto;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.gridiron-caption {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
padding: 2px 5px;
|
||||
border: none !important;
|
||||
}
|
||||
.gridiron-search {
|
||||
float: right;
|
||||
}
|
||||
.gridiron-search-submit {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 4px;
|
||||
}
|
||||
.gridiron-search input {
|
||||
border: 1px solid #999999;
|
||||
width: 10em;
|
||||
height: 1em;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.gridiron-search .ui-icon {
|
||||
display: inline-block !important;
|
||||
}
|
1284
jquery.gridiron.js
Normal file
1284
jquery.gridiron.js
Normal file
File diff suppressed because it is too large
Load Diff
37
jquery.ui.core.css
vendored
Normal file
37
jquery.ui.core.css
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* jQuery UI CSS Framework
|
||||
* Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
|
||||
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses.
|
||||
*/
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
.ui-helper-hidden { display: none; }
|
||||
.ui-helper-hidden-accessible { position: absolute; left: -99999999px; }
|
||||
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
|
||||
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
|
||||
.ui-helper-clearfix { display: inline-block; }
|
||||
/* required comment for clearfix to work in Opera \*/
|
||||
* html .ui-helper-clearfix { height:1%; }
|
||||
.ui-helper-clearfix { display:block; }
|
||||
/* end clearfix */
|
||||
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
|
||||
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-disabled { cursor: default !important; }
|
||||
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
BIN
loading.gif
Normal file
BIN
loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 673 B |
97
test.html
Normal file
97
test.html
Normal file
@ -0,0 +1,97 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
|
||||
<head>
|
||||
<title>jQuery GridIron test</title>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
|
||||
<link rel="stylesheet" href="jquery.gridiron.css" type="text/css" />
|
||||
<link type="text/css" rel="stylesheet" href="jquery.ui.core.css" />
|
||||
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
|
||||
<script type="text/javascript" src="jquery.gridiron.js"></script>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
|
||||
$(document).ready(function() {
|
||||
var countryFormatter = function(columnIndex, column, value, data) {
|
||||
return $("<a/>")
|
||||
.attr("title", "population: " + data.countryPopulation)
|
||||
.attr("href", "http://en.wikipedia.org/wiki/" + value)
|
||||
.text(value);
|
||||
}
|
||||
|
||||
var settings = {
|
||||
columns: [
|
||||
{ displayText: "City", name: "city", width: 150, sortable: true },
|
||||
{ displayText: "Country", name: "country", width: 150, sortable: true, formatter: countryFormatter },
|
||||
{ displayText: "Region", name: "region", width: 150, sortable: true },
|
||||
{ displayText: "Latitude", name: "latitude", width: 75, sortable: true },
|
||||
{ displayText: "Longitude", name: "longitude", width: 75 }
|
||||
],
|
||||
rowsPerPage: 50,
|
||||
enableOverlay: true,
|
||||
height: 350,
|
||||
highlight: "row",
|
||||
select: "row"
|
||||
};
|
||||
|
||||
var settings1 = $.extend({}, settings, {
|
||||
virtualScrolling: true,
|
||||
ajax: {
|
||||
enabled: true,
|
||||
url: "citiestojson.php"
|
||||
},
|
||||
title: "Cities around the world",
|
||||
showSearchBar: true
|
||||
});
|
||||
|
||||
var settings2 = $.extend({}, settings, { title: "Selected cities" });
|
||||
|
||||
var $grid1 = $("#grid1").gridIron(settings1);
|
||||
var $grid2 = $("#grid2").gridIron(settings2);
|
||||
|
||||
$grid1.bind("search", function(e, searchQuery) {
|
||||
$grid1.gridIronApi.ajax.sendAndResetFromCurrentOffset();
|
||||
});
|
||||
|
||||
$("#picklist-add").click(function() {
|
||||
$.each($grid1.gridIronApi.getSelectedRows(), function(i, row) {
|
||||
$grid2.gridIronApi.insertRow(row.data, row.rowId);
|
||||
$grid1.gridIronApi.findRowById(row.rowId).deselect().disable();
|
||||
});
|
||||
});
|
||||
|
||||
$("#picklist-remove").click(function() {
|
||||
$.each($grid2.gridIronApi.getSelectedRows(), function(i, row) {
|
||||
$grid2.gridIronApi.deleteRowById(row.rowId);
|
||||
|
||||
//cannot use findRowById() because the rows could have been wiped out if a search occurred
|
||||
var foundRows = $grid1.gridIronApi.findRows(function(r) {
|
||||
return r.data.city === row.data.city && r.data.latitude === row.data.latitude && r.data.longitude === row.data.longitude;
|
||||
});
|
||||
|
||||
if (foundRows.length) {
|
||||
$.each(foundRows, function(i, r) { r.enable(); });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#themeswitcher").themeswitcher({ loadTheme: "South Street" });
|
||||
});
|
||||
|
||||
//]]></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="themeswitcher" style="margin-bottom: 20px"></div>
|
||||
|
||||
<div id="grid1" style="float: left"></div>
|
||||
|
||||
<div style="width: 50px; margin: 130px 20px 0 20px; float: left">
|
||||
<input type="button" id="picklist-add" value="→"/><br />
|
||||
<input type="button" id="picklist-remove" value="←"/>
|
||||
</div>
|
||||
|
||||
<div id="grid2" style="float: left"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
73
tests/GridIronTest.php
Normal file
73
tests/GridIronTest.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace GridIronTests;
|
||||
|
||||
use PHPUnit_Extensions_SeleniumTestCase;
|
||||
|
||||
require_once 'PHPUnit/Framework.php';
|
||||
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
|
||||
|
||||
class GridIronTest extends PHPUnit_Extensions_SeleniumTestCase {
|
||||
|
||||
private static $grid = '//div[@id="grid1"]';
|
||||
private static $rowContainer = '';
|
||||
private static $caption = '';
|
||||
private static $header = '';
|
||||
private static $footer = '';
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
//phpunit fail! this never gets called
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
self::$rowContainer = self::$grid . '//div[contains(@class, "gridiron-row-container")]';
|
||||
self::$caption = self::$grid . '/div[contains(@class, "gridiron-caption")]';
|
||||
self::$header = self::$grid . '/div[contains(@class, "gridiron-header")]';
|
||||
|
||||
$this->setBrowser('*firefox C:\lib\firefox\firefox.exe');
|
||||
$this->setBrowserUrl('http://localhost/');
|
||||
}
|
||||
|
||||
private function openAndWaitForData() {
|
||||
$this->open('http://localhost/gridiron/test.html');
|
||||
$this->waitForCondition('selenium.browserbot.getCurrentWindow().$("#grid1 .gridiron-data-container .gridiron-row").length > 0;', 2000);
|
||||
}
|
||||
|
||||
public function testCanSelectAndDeselectRow() {
|
||||
$this->openAndWaitForData();
|
||||
$this->click(self::$rowContainer . '/div[1]');
|
||||
$this->assertTrue($this->isElementPresent(self::$rowContainer . '/div[1][contains(@class, "gridiron-selected")]'));
|
||||
$this->click(self::$rowContainer . '/div[1]');
|
||||
$this->assertFalse($this->isElementPresent(self::$rowContainer . '/div[1][contains(@class, "gridiron-selected")]'));
|
||||
}
|
||||
|
||||
public function testCaptionTitleIsVisible() {
|
||||
$this->openAndWaitForData();
|
||||
$this->assertTrue($this->isElementPresent(self::$caption . '/span[contains(@class, "gridiron-title")]'));
|
||||
$this->assertEquals('Cities around the world', $this->getText(self::$caption . '/span[contains(@class, "gridiron-title")]'));
|
||||
}
|
||||
|
||||
public function testColumnsAreRenderedCorrectly() {
|
||||
$this->openAndWaitForData();
|
||||
|
||||
$expectedText = array('City', 'Country', 'Region', 'Latitude', 'Longitude');
|
||||
|
||||
$this->verifyColumnHeader(1, 'City', true, 145);
|
||||
$this->verifyColumnHeader(2, 'Country', true, 145);
|
||||
$this->verifyColumnHeader(3, 'Region', true, 145);
|
||||
$this->verifyColumnHeader(4, 'Latitude', true, 70);
|
||||
$this->verifyColumnHeader(5, 'Longitude', false, 70);
|
||||
}
|
||||
|
||||
private function verifyColumnHeader($index, $text, $sortable, $width) {
|
||||
$xpath = self::$header . '/div[contains(@class, "gridiron-cell")][%s]';
|
||||
|
||||
self::assertTrue($this->isElementPresent(sprintf($xpath, $index)));
|
||||
self::assertEquals($text, $this->getText(sprintf($xpath, $index)));
|
||||
self::assertContains('width: ' . $width . 'px', $this->getAttribute(sprintf($xpath . '/@style', $index)));
|
||||
self::assertEquals($sortable, $this->isElementPresent(sprintf($xpath . '/span[contains(@class, "gridiron-sort-indicator")]', $index)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
Loading…
Reference in New Issue
Block a user