Commit a5ec8f618e7b9f51e511840a0168b7996dea3b7d

Authored by Dan Croak
1 parent 23d34bd5

added under construction (mile marker for jQuery) plugin

app/views/layouts/application.html.erb
... ... @@ -5,6 +5,7 @@
5 5 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
6 6 <title>CHANGEME</title>
7 7 <%= stylesheet_link_tag 'screen', :media => 'all', :cache => true %>
  8 + <%# stylesheet_link_tag 'under_construction' %>
8 9 </head>
9 10 <body class="<%= body_class %>">
10 11 <div class="main">
... ...
public/javascripts/jquery.hotkeys.js 0 → 100644
... ... @@ -0,0 +1,244 @@
  1 +/*
  2 +(c) Copyrights 2007 - 2008
  3 +
  4 +Original idea by by Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
  5 +
  6 +jQuery Plugin by Tzury Bar Yochay
  7 +tzury.by@gmail.com
  8 +http://evalinux.wordpress.com
  9 +http://facebook.com/profile.php?id=513676303
  10 +
  11 +Project's sites:
  12 +http://code.google.com/p/js-hotkeys/
  13 +http://github.com/tzuryby/hotkeys/tree/master
  14 +
  15 +License: same as jQuery license.
  16 +
  17 +USAGE:
  18 + // simple usage
  19 + $(document).bind('keydown', 'Ctrl+c', function(){ alert('copy anyone?');});
  20 +
  21 + // special options such as disableInIput
  22 + $(document).bind('keydown', {combi:'Ctrl+x', disableInInput: true} , function() {});
  23 +
  24 +Note:
  25 + This plugin wraps the following jQuery methods: $.fn.find, $.fn.bind and $.fn.unbind
  26 +*/
  27 +
  28 +(function (jQuery){
  29 + // keep reference to the original $.fn.bind, $.fn.unbind and $.fn.find
  30 + jQuery.fn.__bind__ = jQuery.fn.bind;
  31 + jQuery.fn.__unbind__ = jQuery.fn.unbind;
  32 + jQuery.fn.__find__ = jQuery.fn.find;
  33 +
  34 + var hotkeys = {
  35 + version: '0.7.9',
  36 + override: /keypress|keydown|keyup/g,
  37 + triggersMap: {},
  38 +
  39 + specialKeys: { 27: 'esc', 9: 'tab', 32:'space', 13: 'return', 8:'backspace', 145: 'scroll',
  40 + 20: 'capslock', 144: 'numlock', 19:'pause', 45:'insert', 36:'home', 46:'del',
  41 + 35:'end', 33: 'pageup', 34:'pagedown', 37:'left', 38:'up', 39:'right',40:'down',
  42 + 109: '-',
  43 + 112:'f1',113:'f2', 114:'f3', 115:'f4', 116:'f5', 117:'f6', 118:'f7', 119:'f8',
  44 + 120:'f9', 121:'f10', 122:'f11', 123:'f12', 191: '/'},
  45 +
  46 + shiftNums: { "`":"~", "1":"!", "2":"@", "3":"#", "4":"$", "5":"%", "6":"^", "7":"&",
  47 + "8":"*", "9":"(", "0":")", "-":"_", "=":"+", ";":":", "'":"\"", ",":"<",
  48 + ".":">", "/":"?", "\\":"|" },
  49 +
  50 + newTrigger: function (type, combi, callback) {
  51 + // i.e. {'keyup': {'ctrl': {cb: callback, disableInInput: false}}}
  52 + var result = {};
  53 + result[type] = {};
  54 + result[type][combi] = {cb: callback, disableInInput: false};
  55 + return result;
  56 + }
  57 + };
  58 + // add firefox num pad char codes
  59 + //if (jQuery.browser.mozilla){
  60 + // add num pad char codes
  61 + hotkeys.specialKeys = jQuery.extend(hotkeys.specialKeys, { 96: '0', 97:'1', 98: '2', 99:
  62 + '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9', 106: '*',
  63 + 107: '+', 109: '-', 110: '.', 111 : '/'
  64 + });
  65 + //}
  66 +
  67 + // a wrapper around of $.fn.find
  68 + // see more at: http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d
  69 + jQuery.fn.find = function( selector ) {
  70 + this.query = selector;
  71 + return jQuery.fn.__find__.apply(this, arguments);
  72 + };
  73 +
  74 + jQuery.fn.unbind = function (type, combi, fn){
  75 + if (jQuery.isFunction(combi)){
  76 + fn = combi;
  77 + combi = null;
  78 + }
  79 + if (combi && typeof combi === 'string'){
  80 + var selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
  81 + var hkTypes = type.split(' ');
  82 + for (var x=0; x<hkTypes.length; x++){
  83 + delete hotkeys.triggersMap[selectorId][hkTypes[x]][combi];
  84 + }
  85 + }
  86 + // call jQuery original unbind
  87 + return this.__unbind__(type, fn);
  88 + };
  89 +
  90 + jQuery.fn.bind = function(type, data, fn){
  91 + // grab keyup,keydown,keypress
  92 + var handle = type.match(hotkeys.override);
  93 +
  94 + if (jQuery.isFunction(data) || !handle){
  95 + // call jQuery.bind only
  96 + return this.__bind__(type, data, fn);
  97 + }
  98 + else{
  99 + // split the job
  100 + var result = null,
  101 + // pass the rest to the original $.fn.bind
  102 + pass2jq = jQuery.trim(type.replace(hotkeys.override, ''));
  103 +
  104 + // see if there are other types, pass them to the original $.fn.bind
  105 + if (pass2jq){
  106 + result = this.__bind__(pass2jq, data, fn);
  107 + }
  108 +
  109 + if (typeof data === "string"){
  110 + data = {'combi': data};
  111 + }
  112 + if(data.combi){
  113 + for (var x=0; x < handle.length; x++){
  114 + var eventType = handle[x];
  115 + var combi = data.combi.toLowerCase(),
  116 + trigger = hotkeys.newTrigger(eventType, combi, fn),
  117 + selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
  118 +
  119 + //trigger[eventType][combi].propagate = data.propagate;
  120 + trigger[eventType][combi].disableInInput = data.disableInInput;
  121 +
  122 + // first time selector is bounded
  123 + if (!hotkeys.triggersMap[selectorId]) {
  124 + hotkeys.triggersMap[selectorId] = trigger;
  125 + }
  126 + // first time selector is bounded with this type
  127 + else if (!hotkeys.triggersMap[selectorId][eventType]) {
  128 + hotkeys.triggersMap[selectorId][eventType] = trigger[eventType];
  129 + }
  130 + // make trigger point as array so more than one handler can be bound
  131 + var mapPoint = hotkeys.triggersMap[selectorId][eventType][combi];
  132 + if (!mapPoint){
  133 + hotkeys.triggersMap[selectorId][eventType][combi] = [trigger[eventType][combi]];
  134 + }
  135 + else if (mapPoint.constructor !== Array){
  136 + hotkeys.triggersMap[selectorId][eventType][combi] = [mapPoint];
  137 + }
  138 + else {
  139 + hotkeys.triggersMap[selectorId][eventType][combi][mapPoint.length] = trigger[eventType][combi];
  140 + }
  141 +
  142 + // add attribute and call $.event.add per matched element
  143 + this.each(function(){
  144 + // jQuery wrapper for the current element
  145 + var jqElem = jQuery(this);
  146 +
  147 + // element already associated with another collection
  148 + if (jqElem.attr('hkId') && jqElem.attr('hkId') !== selectorId){
  149 + selectorId = jqElem.attr('hkId') + ";" + selectorId;
  150 + }
  151 + jqElem.attr('hkId', selectorId);
  152 + });
  153 + result = this.__bind__(handle.join(' '), data, hotkeys.handler)
  154 + }
  155 + }
  156 + return result;
  157 + }
  158 + };
  159 + // work-around for opera and safari where (sometimes) the target is the element which was last
  160 + // clicked with the mouse and not the document event it would make sense to get the document
  161 + hotkeys.findElement = function (elem){
  162 + if (!jQuery(elem).attr('hkId')){
  163 + if (jQuery.browser.opera || jQuery.browser.safari){
  164 + while (!jQuery(elem).attr('hkId') && elem.parentNode){
  165 + elem = elem.parentNode;
  166 + }
  167 + }
  168 + }
  169 + return elem;
  170 + };
  171 + // the event handler
  172 + hotkeys.handler = function(event) {
  173 + var target = hotkeys.findElement(event.currentTarget),
  174 + jTarget = jQuery(target),
  175 + ids = jTarget.attr('hkId');
  176 +
  177 + if(ids){
  178 + ids = ids.split(';');
  179 + var code = event.which,
  180 + type = event.type,
  181 + special = hotkeys.specialKeys[code],
  182 + // prevent f5 overlapping with 't' (or f4 with 's', etc.)
  183 + character = !special && String.fromCharCode(code).toLowerCase(),
  184 + shift = event.shiftKey,
  185 + ctrl = event.ctrlKey,
  186 + // patch for jquery 1.2.5 && 1.2.6 see more at:
  187 + // http://groups.google.com/group/jquery-en/browse_thread/thread/83e10b3bb1f1c32b
  188 + alt = event.altKey || event.originalEvent.altKey,
  189 + mapPoint = null;
  190 +
  191 + for (var x=0; x < ids.length; x++){
  192 + if (hotkeys.triggersMap[ids[x]][type]){
  193 + mapPoint = hotkeys.triggersMap[ids[x]][type];
  194 + break;
  195 + }
  196 + }
  197 +
  198 + //find by: id.type.combi.options
  199 + if (mapPoint){
  200 + var trigger;
  201 + // event type is associated with the hkId
  202 + if(!shift && !ctrl && !alt) { // No Modifiers
  203 + trigger = mapPoint[special] || (character && mapPoint[character]);
  204 + }
  205 + else{
  206 + // check combinations (alt|ctrl|shift+anything)
  207 + var modif = '';
  208 + if(alt) modif +='alt+';
  209 + if(ctrl) modif+= 'ctrl+';
  210 + if(shift) modif += 'shift+';
  211 + // modifiers + special keys or modifiers + character or modifiers + shift character or just shift character
  212 + trigger = mapPoint[modif+special];
  213 + if (!trigger){
  214 + if (character){
  215 + trigger = mapPoint[modif+character]
  216 + || mapPoint[modif+hotkeys.shiftNums[character]]
  217 + // '$' can be triggered as 'Shift+4' or 'Shift+$' or just '$'
  218 + || (modif === 'shift+' && mapPoint[hotkeys.shiftNums[character]]);
  219 + }
  220 + }
  221 + }
  222 + if (trigger){
  223 + var result = false;
  224 + for (var x=0; x < trigger.length; x++){
  225 + if(trigger[x].disableInInput){
  226 + // double check event.currentTarget and event.target
  227 + var elem = jQuery(event.target);
  228 + if (jTarget.is("input") || jTarget.is("textarea") || jTarget.is("select")
  229 + || elem.is("input") || elem.is("textarea") || elem.is("select")) {
  230 + return true;
  231 + }
  232 + }
  233 + // call the registered callback function
  234 + result = result || trigger[x].cb.apply(this, [event]);
  235 + }
  236 + return result;
  237 + }
  238 + }
  239 + }
  240 + };
  241 + // place it under window so it can be extended and overridden by others
  242 + window.hotkeys = hotkeys;
  243 + return jQuery;
  244 +})(jQuery);
... ...
public/javascripts/jquery.under_construction.js 0 → 100644
... ... @@ -0,0 +1,66 @@
  1 +jQuery.under_construction = {
  2 + hidePendingElements : function() {
  3 + jQuery(".pending").each(function(i){
  4 + jQuery(this).addClass("not_visible");
  5 + });
  6 + },
  7 +
  8 + showPendingElements : function() {
  9 + jQuery(".pending").removeClass("not_visible");
  10 + },
  11 +
  12 + hideOverlayOfPendingElements : function() {
  13 + jQuery(".overlay_for_pending").remove();
  14 + },
  15 +
  16 + overlayPendingElements : function() {
  17 + jQuery(".pending").each(function(i){
  18 +
  19 + //clone the element so that we can overlay it
  20 + var overlay_element = jQuery("<div>");
  21 +
  22 + //set the height
  23 + var height = jQuery(this).outerHeight();
  24 + var width = jQuery(this).outerWidth();
  25 +
  26 + jQuery(overlay_element).height(height);
  27 + jQuery(overlay_element).width(width);
  28 +
  29 + //set the position
  30 + var pos = jQuery(this).position();
  31 +
  32 + jQuery(overlay_element).css("top", pos.top);
  33 + jQuery(overlay_element).css("left", pos.left);
  34 +
  35 + //add the class that sets opacity and background color
  36 + jQuery(overlay_element).addClass("overlay_for_pending");
  37 +
  38 + //todo: replace this with a delimited class to indicate what milestone it is
  39 + jQuery(overlay_element).html("");
  40 +
  41 + //insert after the element in question
  42 + jQuery("body").append(overlay_element);
  43 + });
  44 + },
  45 +
  46 + toggleDisplayOfPendingElements : function() {
  47 + if(jQuery(".pending").hasClass("not_visible")) {
  48 + this.showPendingElements();
  49 + }
  50 + else {
  51 + this.hidePendingElements();
  52 + }
  53 + },
  54 +
  55 + toggleOverlayOfPendingElements : function() {
  56 + if(jQuery(".overlay_for_pending").length > 0) {
  57 + this.hideOverlayOfPendingElements();
  58 + }
  59 + else {
  60 + this.overlayPendingElements();
  61 + }
  62 + },
  63 +
  64 +
  65 +}
  66 +
... ...
public/stylesheets/under_construction.css 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +.not_visible {
  2 + display: none !important;
  3 +}
  4 +
  5 +.not_visible.whiteout {
  6 + visibility: hidden !important;
  7 + display: block !important;
  8 +}
  9 +
  10 +.overlay_for_pending {
  11 + opacity: 0.7;
  12 + filter:alpha(opacity=70);
  13 + background-color: #000;
  14 + background-image: none;
  15 + position: absolute;
  16 + z-index: 999;
  17 +}
  18 +
... ...
vendor/plugins/under_construction/readme.textile 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +h1. Under Construction
  2 +
  3 +Show what's in development on your web applications. Inspired by Thoughtbot's MileMarker
  4 +
  5 +h2. Requirements
  6 +
  7 +* JQuery
  8 +
  9 +h2. Example Usage
  10 +
  11 +"View the demo":http://dpickett.github.com/under_construction/demo.html
  12 +
  13 +h2. How to Use
  14 +
  15 +Put the script and stylesheets in your project. Give elements that aren't built yet the class of "pending". You can toggle an overlay or display of these elements using toggleOverlayOfPendingElements() and toggleDisplayOfPendingElements() respectively.
  16 +
  17 +Works great with the "hotkeys plugin":http://code.google.com/p/js-hotkeys/
  18 +
  19 +h2. MIT License
  20 +
  21 +h2. TODO
  22 +
  23 +* Mechanism on the overlay to show when/or in what iteration the feature will be unlocked
  24 +* Improved Documentation
  25 +* QUnit Testing
  26 +* Browser Testing
... ...
vendor/plugins/under_construction/script/jquery.under_construction.js 0 → 100644
... ... @@ -0,0 +1,66 @@
  1 +jQuery.under_construction = {
  2 + hidePendingElements : function() {
  3 + jQuery(".pending").each(function(i){
  4 + jQuery(this).addClass("not_visible");
  5 + });
  6 + },
  7 +
  8 + showPendingElements : function() {
  9 + jQuery(".pending").removeClass("not_visible");
  10 + },
  11 +
  12 + hideOverlayOfPendingElements : function() {
  13 + jQuery(".overlay_for_pending").remove();
  14 + },
  15 +
  16 + overlayPendingElements : function() {
  17 + jQuery(".pending").each(function(i){
  18 +
  19 + //clone the element so that we can overlay it
  20 + var overlay_element = jQuery("<div>");
  21 +
  22 + //set the height
  23 + var height = jQuery(this).outerHeight();
  24 + var width = jQuery(this).outerWidth();
  25 +
  26 + jQuery(overlay_element).height(height);
  27 + jQuery(overlay_element).width(width);
  28 +
  29 + //set the position
  30 + var pos = jQuery(this).position();
  31 +
  32 + jQuery(overlay_element).css("top", pos.top);
  33 + jQuery(overlay_element).css("left", pos.left);
  34 +
  35 + //add the class that sets opacity and background color
  36 + jQuery(overlay_element).addClass("overlay_for_pending");
  37 +
  38 + //todo: replace this with a delimited class to indicate what milestone it is
  39 + jQuery(overlay_element).html("");
  40 +
  41 + //insert after the element in question
  42 + jQuery("body").append(overlay_element);
  43 + });
  44 + },
  45 +
  46 + toggleDisplayOfPendingElements : function() {
  47 + if(jQuery(".pending").hasClass("not_visible")) {
  48 + this.showPendingElements();
  49 + }
  50 + else {
  51 + this.hidePendingElements();
  52 + }
  53 + },
  54 +
  55 + toggleOverlayOfPendingElements : function() {
  56 + if(jQuery(".overlay_for_pending").length > 0) {
  57 + this.hideOverlayOfPendingElements();
  58 + }
  59 + else {
  60 + this.overlayPendingElements();
  61 + }
  62 + },
  63 +
  64 +
  65 +}
  66 +
... ...
vendor/plugins/under_construction/stylesheets/under_construction.css 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +.not_visible {
  2 + display: none !important;
  3 +}
  4 +
  5 +.not_visible.whiteout {
  6 + visibility: hidden !important;
  7 + display: block !important;
  8 +}
  9 +
  10 +.overlay_for_pending {
  11 + opacity: 0.7;
  12 + filter:alpha(opacity=70);
  13 + background-color: #000;
  14 + background-image: none;
  15 + position: absolute;
  16 + z-index: 999;
  17 +}
  18 +
... ...