/**
* hashgrid (jQuery version)
* http://github.com/dotjay/hashgrid
* Version 6, 10 Jun 2011
* Written by Jon Gibbins at Analog, http://analog.coop/
*
* Contibutors:
* Sean Coates, http://seancoates.com/
* Phil Dokas, http://jetless.org/
* Andrew Jaswa, http://andrewjaswa.com/
*/
/**
* @license Copyright 2011 Analog Coop Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Usage
*
* // The basic #grid setup looks like this
* var grid = new hashgrid();
*
* // Or you can set a custom id for your grid, e.g. #mygrid
* var grid = new hashgrid("mygrid");
*
* // But there are a whole bunch of additional options you can set
* var grid = new hashgrid({
* id: 'mygrid', // id for the grid container
* modifierKey: 'alt', // optional 'ctrl', 'alt' or 'shift'
* showGridKey: 's', // key to show the grid
* holdGridKey: 'enter', // key to hold the grid in place
* foregroundKey: 'f', // key to toggle foreground/background
* jumpGridsKey: 'd', // key to cycle through the grid classes
* numberOfGrids: 2, // number of grid classes used
* classPrefix: 'class', // prefix for the grid classes
* cookiePrefix: 'mygrid' // prefix for the cookie name
* });
*/
/**
* Make sure we have the library
* TODO: Use an adapter
*/
if (typeof jQuery == "undefined") {
alert("Hashgrid: jQuery not loaded. Make sure it's linked to your pages.");
}
/**
* hashgrid overlay
* @constructor
*/
var hashgrid = function(set) {
var options = {
id: 'grid', // id for the grid container
modifierKey: null, // optional 'ctrl', 'alt' or 'shift'
showGridKey: 'g', // key to show the grid
holdGridKey: 'h', // key to hold the grid in place
foregroundKey: 'f', // key to toggle foreground/background
jumpGridsKey: 'j', // key to cycle through the grid classes
numberOfGrids: 1, // number of grid classes used
classPrefix: 'grid-', // prefix for the grid classes
cookiePrefix: 'hashgrid'// prefix for the cookie name
},
classNumber = 1,
gridLines,
gridWidth,
i,
line,
lineHeight,
numGridLines = 11,
overlay,
overlayCookie,
overlayEl,
overlayOn = false,
overlayVert,
overlayZState = 'B',
overlayZBackground = -1,
overlayZForeground = 9999,
pageHeight,
setKey,
state,
sticky = false,
top;
// Apply options
if (typeof set == 'object') {
for (setKey in set) {
options[setKey] = set[setKey];
}
}
else if (typeof set == 'string') {
options.id = set;
}
// Remove any conflicting overlay
if ($('#' + options.id).length > 0) {
$('#' + options.id).remove();
}
// Create overlay, hidden before adding to DOM
overlayEl = $('
');
overlayEl
.attr('id', options.id)
.css({
display: 'none',
'pointer-events': 'none'
});
$("body").prepend(overlayEl);
overlay = $('#' + options.id);
// Unless a custom z-index is set, ensure the overlay will be behind everything
if (overlay.css('z-index') == 'auto') overlay.css('z-index', overlayZBackground);
// Override the default overlay height with the actual page height
pageHeight = parseFloat($(document).height());
overlay.height(pageHeight);
// Add the first grid line so that we can measure it
overlay.append('');
// Position off-screen and display to calculate height
top = overlay.css("top");
overlay.css({
top: "-999px",
display: "block"
});
// Calculate the number of grid lines needed
line = $('#' + options.id + '-horiz');
lineHeight = line.outerHeight();
// Hide and reset top
overlay.css({
display: "none",
top: top
});
// Break on zero line height
if (lineHeight <= 0) {
return false;
}
// Add the remaining grid lines
numGridLines = Math.floor(pageHeight / lineHeight);
gridLines = '';
for (i = numGridLines - 1; i >= 1; i--) {
gridLines += '
';
}
overlay.append(gridLines);
// vertical grid
overlay.append($('
'));
overlayVert = overlay.children('.vert-container');
gridWidth = overlay.width();
overlayVert.css({width: gridWidth, position: 'absolute', top: 0});
overlayVert.append('
');
// 30 is an arbitrarily large number...
// can't calculate the margin width properly
gridLines = '';
for (i = 0; i < 30; i++) {
gridLines += '
';
}
overlayVert.append(gridLines);
overlayVert.children()
.height(pageHeight)
.css({ display: 'inline-block' });
// Check for saved state
overlayCookie = readCookie(options.cookiePrefix + options.id);
if (typeof overlayCookie == 'string') {
state = overlayCookie.split(',');
state[2] = Number(state[2]);
if ((typeof state[2] == 'number') && !isNaN(state[2])) {
classNumber = state[2].toFixed(0);
overlay.addClass(options.classPrefix + classNumber);
}
if (state[1] == 'F') {
overlayZState = 'F';
overlay.css('z-index', overlayZForeground);
}
if (state[0] == '1') {
overlayOn = true;
sticky = true;
showOverlay();
}
}
else {
overlay.addClass(options.classPrefix + classNumber);
}
// Keyboard controls
$(document).bind('keydown', keydownHandler);
$(document).bind('keyup', keyupHandler);
/**
* Helpers
*/
function getModifier(e) {
if (options.modifierKey == null) return true; // Bypass by default
var m = true;
switch(options.modifierKey) {
case 'ctrl':
m = (e.ctrlKey ? e.ctrlKey : false);
break;
case 'alt':
m = (e.altKey ? e.altKey : false);
break;
case 'shift':
m = (e.shiftKey ? e.shiftKey : false);
break;
}
return m;
}
function getKey(e) {
var k = false, c = (e.keyCode ? e.keyCode : e.which);
// Handle keywords
if (c == 13) k = 'enter';
// Handle letters
else k = String.fromCharCode(c).toLowerCase();
return k;
}
function saveState() {
createCookie(options.cookiePrefix + options.id, (sticky ? '1' : '0') + ',' + overlayZState + ',' + classNumber, 1);
}
function showOverlay() {
overlay.show();
overlayVert.css({width: overlay.width()});
// hide any vertical blocks that aren't at the top of the viewport
overlayVert.children('.vert').each(function () {
$(this).css('display','inline-block');
if ($(this).offset().top > 0) {
$(this).hide();
}
});
}
/**
* Event handlers
*/
function keydownHandler(e) {
var k,
m,
source = e.target.tagName.toLowerCase();
if ((source == 'input') || (source == 'textarea') || (source == 'select')) {
return true;
}
m = getModifier(e);
if (!m) {
return true;
}
k = getKey(e);
if (!k) {
return true;
}
switch(k) {
case options.showGridKey:
if (!overlayOn) {
showOverlay();
overlayOn = true;
}
else if (sticky) {
overlay.hide();
overlayOn = false;
sticky = false;
saveState();
}
break;
case options.holdGridKey:
if (overlayOn && !sticky) {
// Turn sticky overlay on
sticky = true;
saveState();
}
break;
case options.foregroundKey:
if (overlayOn) {
// Toggle sticky overlay z-index
if (overlay.css('z-index') == overlayZForeground) {
overlay.css('z-index', overlayZBackground);
overlayZState = 'B';
}
else {
overlay.css('z-index', overlayZForeground);
overlayZState = 'F';
}
saveState();
}
break;
case options.jumpGridsKey:
if (overlayOn && (options.numberOfGrids > 1)) {
// Cycle through the available grids
overlay.removeClass(options.classPrefix + classNumber);
classNumber++;
if (classNumber > options.numberOfGrids) classNumber = 1;
overlay.addClass(options.classPrefix + classNumber);
showOverlay();
if (/webkit/.test( navigator.userAgent.toLowerCase() )) {
forceRepaint();
}
saveState();
}
break;
}
return true;
}
function keyupHandler(e) {
var k,
m = getModifier(e);
if (!m) {
return true;
}
k = getKey(e);
if (k && (k == options.showGridKey) && !sticky) {
overlay.hide();
overlayOn = false;
}
return true;
}
/**
* Cookie functions
*
* By Peter-Paul Koch:
* http://www.quirksmode.org/js/cookies.html
*/
function createCookie(name, value, days) {
var date,
expires = "";
if (days) {
date = new Date();
date.setTime( date.getTime() + (days*24*60*60*1000) );
expires = "; expires=" + date.toGMTString();
}
document.cookie = name + "=" + value + expires + "; path=/";
}
function readCookie(name) {
var c,
ca = document.cookie.split(';'),
i = 0,
len = ca.length,
nameEQ = name + "=";
for (; i < len; i++) {
c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) == 0) {
return c.substring(nameEQ.length, c.length);
}
}
return null;
}
function eraseCookie(name) {
createCookie(name, "", -1);
}
/**
* Forces a repaint (because WebKit has issues)
* http://www.sitepoint.com/forums/showthread.php?p=4538763
* http://www.phpied.com/the-new-game-show-will-it-reflow/
*/
function forceRepaint() {
var ss = document.styleSheets[0];
try {
ss.addRule('.xxxxxx', 'position: relative');
ss.removeRule(ss.rules.length - 1);
} catch(e) {}
}
return {};
};
/**
* You can call hashgrid from your own code, but it's loaded here as
* an example for your convenience.
*/
$(document).ready(function() {
var grid = new hashgrid({
numberOfGrids: 2
});
});