Commit a5ec8f618e7b9f51e511840a0168b7996dea3b7d
1 parent
23d34bd5
Exists in
master
and in
1 other branch
added under construction (mile marker for jQuery) plugin
Showing
7 changed files
with
439 additions
and
0 deletions
Show diff stats
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"> | ... | ... |
... | ... | @@ -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); | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |