This commit is contained in:
tmont 2021-01-02 22:16:37 -08:00
commit 0b97eaffad
9 changed files with 1879 additions and 0 deletions

111
citiestojson.php Normal file
View 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

File diff suppressed because one or more lines are too long

35
index.html Normal file
View 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&rsquo;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&rsquo; 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
View 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

File diff suppressed because it is too large Load Diff

37
jquery.ui.core.css vendored Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

97
test.html Normal file
View 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="&rarr;"/><br />
<input type="button" id="picklist-remove" value="&larr;"/>
</div>
<div id="grid2" style="float: left"></div>
</body>
</html>

73
tests/GridIronTest.php Normal file
View 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)));
}
}
?>