merb-ui 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/merb-ui.rb ADDED
@@ -0,0 +1,77 @@
1
+ if defined?(Merb::Plugins)
2
+
3
+ $:.unshift File.dirname(__FILE__)
4
+
5
+ load_dependencies('merb-slices', 'merb-helpers')
6
+ Merb::Plugins.add_rakefiles "merb-ui/merbtasks", "merb-ui/slicetasks", "merb-ui/spectasks"
7
+
8
+ # Register the Slice for the current host application
9
+ Merb::Slices::register(__FILE__)
10
+
11
+ # Slice configuration - set this in a before_app_loads callback.
12
+ # By default a Slice uses its own layout, so you can swicht to
13
+ # the main application layout or no layout at all if needed.
14
+ #
15
+ # Configuration options:
16
+ # :layout - the layout to use; defaults to :merb-ui
17
+ # :mirror - which path component types to use on copy operations; defaults to all
18
+ Merb::Slices::config[:merb_ui][:layout] ||= :application
19
+
20
+ # All Slice code is expected to be namespaced inside a module
21
+ module MerbUi
22
+
23
+ # Slice metadata
24
+ self.description = "Merb UI"
25
+ self.version = "1.0"
26
+ self.author = "UI Poet"
27
+
28
+ # Stub classes loaded hook - runs before LoadClasses BootLoader
29
+ # right after a slice's classes have been loaded internally.
30
+ def self.loaded
31
+ end
32
+
33
+ # Initialization hook - runs before AfterAppLoads BootLoader
34
+ def self.init
35
+ end
36
+
37
+ # Activation hook - runs after AfterAppLoads BootLoader
38
+ def self.activate
39
+ end
40
+
41
+ # Deactivation hook - triggered by Merb::Slices.deactivate(MerbUi)
42
+ def self.deactivate
43
+ end
44
+
45
+ # Setup routes inside the host application
46
+ #
47
+ # @param scope<Merb::Router::Behaviour>
48
+ # Routes will be added within this scope (namespace). In fact, any
49
+ # router behaviour is a valid namespace, so you can attach
50
+ # routes at any level of your router setup.
51
+ #
52
+ # @note prefix your named routes with :merb_ui_
53
+ # to avoid potential conflicts with global named routes.
54
+ def self.setup_router(scope)
55
+ scope.match('/stylesheets/mui.css').to(:controller => 'styles', :action => 'index')
56
+ end
57
+
58
+ end
59
+
60
+ # Setup the slice layout for MerbUi
61
+ #
62
+ # Use MerbUi.push_path and MerbUi.push_app_path
63
+ # to set paths to merb-ui-level and app-level paths. Example:
64
+ #
65
+ # MerbUi.push_path(:application, MerbUi.root)
66
+ # MerbUi.push_app_path(:application, Merb.root / 'slices' / 'merb-ui')
67
+ # ...
68
+ #
69
+ # Any component path that hasn't been set will default to MerbUi.root
70
+ #
71
+ # Or just call setup_default_structure! to setup a basic Merb MVC structure.
72
+ MerbUi.setup_default_structure!
73
+
74
+ # Add dependencies for other MerbUi classes below. Example:
75
+ # dependency "merb-ui/other"
76
+
77
+ end
@@ -0,0 +1,103 @@
1
+ namespace :slices do
2
+ namespace :merb_ui do
3
+
4
+ desc "Install MerbUi"
5
+ task :install => [:preflight, :setup_directories, :copy_assets, :migrate]
6
+
7
+ desc "Test for any dependencies"
8
+ task :preflight do # see slicetasks.rb
9
+ end
10
+
11
+ desc "Setup directories"
12
+ task :setup_directories do
13
+ puts "Creating directories for host application"
14
+ MerbUi.mirrored_components.each do |type|
15
+ if File.directory?(MerbUi.dir_for(type))
16
+ if !File.directory?(dst_path = MerbUi.app_dir_for(type))
17
+ relative_path = dst_path.relative_path_from(Merb.root)
18
+ puts "- creating directory :#{type} #{File.basename(Merb.root) / relative_path}"
19
+ mkdir_p(dst_path)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ desc "Copy stub files to host application"
26
+ task :stubs do
27
+ puts "Copying stubs for MerbUi - resolves any collisions"
28
+ copied, preserved = MerbUi.mirror_stubs!
29
+ puts "- no files to copy" if copied.empty? && preserved.empty?
30
+ copied.each { |f| puts "- copied #{f}" }
31
+ preserved.each { |f| puts "! preserved override as #{f}" }
32
+ end
33
+
34
+ desc "Copy stub files and views to host application"
35
+ task :patch => [ "stubs", "freeze:views" ]
36
+
37
+ desc "Copy public assets to host application"
38
+ task :copy_assets do
39
+ puts "Copying assets for MerbUi - resolves any collisions"
40
+ copied, preserved = MerbUi.mirror_public!
41
+ puts "- no files to copy" if copied.empty? && preserved.empty?
42
+ copied.each { |f| puts "- copied #{f}" }
43
+ preserved.each { |f| puts "! preserved override as #{f}" }
44
+ end
45
+
46
+ desc "Migrate the database"
47
+ task :migrate do # see slicetasks.rb
48
+ end
49
+
50
+ desc "Freeze MerbUi into your app (only merb-ui/app)"
51
+ task :freeze => [ "freeze:app" ]
52
+
53
+ namespace :freeze do
54
+
55
+ desc "Freezes MerbUi by installing the gem into application/gems"
56
+ task :gem do
57
+ ENV["GEM"] ||= "merb-ui"
58
+ Rake::Task['slices:install_as_gem'].invoke
59
+ end
60
+
61
+ desc "Freezes MerbUi by copying all files from merb-ui/app to your application"
62
+ task :app do
63
+ puts "Copying all merb-ui/app files to your application - resolves any collisions"
64
+ copied, preserved = MerbUi.mirror_app!
65
+ puts "- no files to copy" if copied.empty? && preserved.empty?
66
+ copied.each { |f| puts "- copied #{f}" }
67
+ preserved.each { |f| puts "! preserved override as #{f}" }
68
+ end
69
+
70
+ desc "Freeze all views into your application for easy modification"
71
+ task :views do
72
+ puts "Copying all view templates to your application - resolves any collisions"
73
+ copied, preserved = MerbUi.mirror_files_for :view
74
+ puts "- no files to copy" if copied.empty? && preserved.empty?
75
+ copied.each { |f| puts "- copied #{f}" }
76
+ preserved.each { |f| puts "! preserved override as #{f}" }
77
+ end
78
+
79
+ desc "Freeze all models into your application for easy modification"
80
+ task :models do
81
+ puts "Copying all models to your application - resolves any collisions"
82
+ copied, preserved = MerbUi.mirror_files_for :model
83
+ puts "- no files to copy" if copied.empty? && preserved.empty?
84
+ copied.each { |f| puts "- copied #{f}" }
85
+ preserved.each { |f| puts "! preserved override as #{f}" }
86
+ end
87
+
88
+ desc "Freezes MerbUi as a gem and copies over merb-ui/app"
89
+ task :app_with_gem => [:gem, :app]
90
+
91
+ desc "Freezes MerbUi by unpacking all files into your application"
92
+ task :unpack do
93
+ puts "Unpacking MerbUi files to your application - resolves any collisions"
94
+ copied, preserved = MerbUi.unpack_slice!
95
+ puts "- no files to copy" if copied.empty? && preserved.empty?
96
+ copied.each { |f| puts "- copied #{f}" }
97
+ preserved.each { |f| puts "! preserved override as #{f}" }
98
+ end
99
+
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,18 @@
1
+ namespace :slices do
2
+ namespace :merb_ui do
3
+
4
+ # add your own merb-ui tasks here
5
+
6
+ # implement this to test for structural/code dependencies
7
+ # like certain directories or availability of other files
8
+ desc "Test for any dependencies"
9
+ task :preflight do
10
+ end
11
+
12
+ # implement this to perform any database related setup steps
13
+ desc "Migrate the database"
14
+ task :migrate do
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,65 @@
1
+ namespace :slices do
2
+ namespace :merb_ui do
3
+
4
+ desc "Run slice specs within the host application context"
5
+ task :spec => [ "spec:explain", "spec:default" ]
6
+
7
+ namespace :spec do
8
+
9
+ slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
10
+
11
+ task :explain do
12
+ puts "\nNote: By running MerbUi specs inside the application context any\n" +
13
+ "overrides could break existing specs. This isn't always a problem,\n" +
14
+ "especially in the case of views. Use these spec tasks to check how\n" +
15
+ "well your application conforms to the original slice implementation."
16
+ end
17
+
18
+ Spec::Rake::SpecTask.new('default') do |t|
19
+ t.spec_opts = ["--format", "specdoc", "--colour"]
20
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
21
+ end
22
+
23
+ desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel"
24
+ Spec::Rake::SpecTask.new('model') do |t|
25
+ t.spec_opts = ["--format", "specdoc", "--colour"]
26
+ if(ENV['MODEL'])
27
+ t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort
28
+ else
29
+ t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort
30
+ end
31
+ end
32
+
33
+ desc "Run all controller specs, run a spec for a specific Controller with CONTROLLER=MyController"
34
+ Spec::Rake::SpecTask.new('controller') do |t|
35
+ t.spec_opts = ["--format", "specdoc", "--colour"]
36
+ if(ENV['CONTROLLER'])
37
+ t.spec_files = Dir["#{slice_root}/spec/controllers/**/#{ENV['CONTROLLER']}_spec.rb"].sort
38
+ else
39
+ t.spec_files = Dir["#{slice_root}/spec/controllers/**/*_spec.rb"].sort
40
+ end
41
+ end
42
+
43
+ desc "Run all view specs, run specs for a specific controller (and view) with CONTROLLER=MyController (VIEW=MyView)"
44
+ Spec::Rake::SpecTask.new('view') do |t|
45
+ t.spec_opts = ["--format", "specdoc", "--colour"]
46
+ if(ENV['CONTROLLER'] and ENV['VIEW'])
47
+ t.spec_files = Dir["#{slice_root}/spec/views/**/#{ENV['CONTROLLER']}/#{ENV['VIEW']}*_spec.rb"].sort
48
+ elsif(ENV['CONTROLLER'])
49
+ t.spec_files = Dir["#{slice_root}/spec/views/**/#{ENV['CONTROLLER']}/*_spec.rb"].sort
50
+ else
51
+ t.spec_files = Dir["#{slice_root}/spec/views/**/*_spec.rb"].sort
52
+ end
53
+ end
54
+
55
+ desc "Run all specs and output the result in html"
56
+ Spec::Rake::SpecTask.new('html') do |t|
57
+ t.spec_opts = ["--format", "html"]
58
+ t.libs = ['lib', 'server/lib' ]
59
+ t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
Binary file
Binary file
Binary file
@@ -0,0 +1,84 @@
1
+ // Position message at top center.
2
+ function messagePosition(){
3
+ var browserWidth = $(window).width();
4
+ var messageWidth = $('.mui_message').width();
5
+ $('.mui_message').css({position:'fixed', left:(browserWidth/2)+(messageWidth/2)*(-1), top:0}).slideDown('fast');
6
+ }
7
+
8
+ // Close window.
9
+ function windowClose(url){
10
+ var target = $('.mui_window_target');
11
+ target.fadeOut(function(){
12
+ target.empty;
13
+ $('.mui_focus:first').focus();
14
+ });
15
+ }
16
+
17
+ // Open window and give the first input with class of mui_focus focus, if one exists.
18
+ function windowOpen(url){
19
+ var target = $('.mui_window_target');
20
+ target.fadeOut(function(){
21
+ target.load(url, function(){
22
+ target.fadeIn();
23
+ $('.mui_focus:first').focus();
24
+ });
25
+ });
26
+ }
27
+
28
+ // Position window at middle center.
29
+ function windowPosition(){
30
+ var browserWidth = $(window).width();
31
+ var browserHeight = $(window).height();
32
+ var windowWidth = $('.mui_window_target').width();
33
+ var windowHeight = $('.mui_window_target').height();
34
+ if (windowWidth > browserWidth){
35
+ $('.mui_window_target').css({position:'absolute', left:'1em'});
36
+ }
37
+ else{
38
+ $('.mui_window_target').css({position:'fixed', left:(browserWidth/2)+(windowWidth/2)*(-1)});
39
+ }
40
+ if (windowHeight > browserHeight){
41
+ $('.mui_window_target').css({position:'absolute', top:'1em'});
42
+ }
43
+ else{
44
+ $('.mui_window_target').css({position:'fixed', top:(browserHeight/2)+(windowHeight/2)*(-1)});
45
+ }
46
+ }
47
+
48
+ // Bind buttons.
49
+ $(function(){
50
+ $('.mui_click').click(function(){
51
+ window.location = this.id;
52
+ });
53
+ $('.mui_click_window_open').click(function(){
54
+ windowOpen(this.id);
55
+ });
56
+ $('.mui_click_message_close').click(function(){
57
+ $('.mui_message').slideUp('fast', function(){
58
+ $('.mui_message').remove();
59
+ });
60
+ });
61
+ messagePosition();
62
+ });
63
+
64
+ // Bind keyboard shortcuts.
65
+ $(document).keybind('ctrl+shift+P', function(){
66
+ windowOpen('/password/read');
67
+ });
68
+ $(document).keybind('ctrl+shift+esc', function(){
69
+ window.location = '/password/exit';
70
+ });
71
+ $(document).keybind('esc', function(){
72
+ if ($('.mui_message').length > 0){
73
+ $('.mui_click_message_close').click();
74
+ }
75
+ else{
76
+ $('.mui_click_window_close').click();
77
+ }
78
+ });
79
+ $(document).keybind('left', function(){
80
+ $('[name=previous]').click();
81
+ });
82
+ $(document).keybind('right', function(){
83
+ $('[name=next]').click();
84
+ });
@@ -0,0 +1,12 @@
1
+ /* Copyright (c) 2007 Paul Bakaus (paul.bakaus@googlemail.com) and Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
2
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
3
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
4
+ *
5
+ * $LastChangedDate: 2007-12-20 08:43:48 -0600 (Thu, 20 Dec 2007) $
6
+ * $Rev: 4257 $
7
+ *
8
+ * Version: 1.2
9
+ *
10
+ * Requires: jQuery 1.2+
11
+ */
12
+ eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(5($){$.19={P:\'1.2\'};$.u([\'j\',\'w\'],5(i,d){$.q[\'O\'+d]=5(){p(!3[0])6;g a=d==\'j\'?\'s\':\'m\',e=d==\'j\'?\'D\':\'C\';6 3.B(\':y\')?3[0][\'L\'+d]:4(3,d.x())+4(3,\'n\'+a)+4(3,\'n\'+e)};$.q[\'I\'+d]=5(b){p(!3[0])6;g c=d==\'j\'?\'s\':\'m\',e=d==\'j\'?\'D\':\'C\';b=$.F({t:Z},b||{});g a=3.B(\':y\')?3[0][\'8\'+d]:4(3,d.x())+4(3,\'E\'+c+\'w\')+4(3,\'E\'+e+\'w\')+4(3,\'n\'+c)+4(3,\'n\'+e);6 a+(b.t?(4(3,\'t\'+c)+4(3,\'t\'+e)):0)}});$.u([\'m\',\'s\'],5(i,b){$.q[\'l\'+b]=5(a){p(!3[0])6;6 a!=W?3.u(5(){3==h||3==r?h.V(b==\'m\'?a:$(h)[\'U\'](),b==\'s\'?a:$(h)[\'T\']()):3[\'l\'+b]=a}):3[0]==h||3[0]==r?S[(b==\'m\'?\'R\':\'Q\')]||$.N&&r.M[\'l\'+b]||r.A[\'l\'+b]:3[0][\'l\'+b]}});$.q.F({z:5(){g a=0,f=0,o=3[0],8,9,7,v;p(o){7=3.7();8=3.8();9=7.8();8.f-=4(o,\'K\');8.k-=4(o,\'J\');9.f+=4(7,\'H\');9.k+=4(7,\'Y\');v={f:8.f-9.f,k:8.k-9.k}}6 v},7:5(){g a=3[0].7;G(a&&(!/^A|10$/i.16(a.15)&&$.14(a,\'z\')==\'13\'))a=a.7;6 $(a)}});5 4(a,b){6 12($.11(a.17?a[0]:a,b,18))||0}})(X);',62,72,'|||this|num|function|return|offsetParent|offset|parentOffset|||||borr|top|var|window||Height|left|scroll|Left|padding|elem|if|fn|document|Top|margin|each|results|Width|toLowerCase|visible|position|body|is|Right|Bottom|border|extend|while|borderTopWidth|outer|marginLeft|marginTop|client|documentElement|boxModel|inner|version|pageYOffset|pageXOffset|self|scrollTop|scrollLeft|scrollTo|undefined|jQuery|borderLeftWidth|false|html|curCSS|parseInt|static|css|tagName|test|jquery|true|dimensions'.split('|'),0,{}))
@@ -0,0 +1,138 @@
1
+ // Look, I'm like all the cool kids!
2
+ ;(function(_) {
3
+ _.special_keys = {
4
+ 27:'esc',
5
+ 9:'tab',
6
+ 32:'space',
7
+ 13:'enter',
8
+ 8:'backspace',
9
+
10
+ 145:'scroll_lock',
11
+ 20:'caps_lock',
12
+ 144:'num_lock',
13
+
14
+ 19:'pause',
15
+
16
+ 45:'insert',
17
+ 36:'home',
18
+ 46:'delete',
19
+ 35:'end',
20
+
21
+ 33:'page_up',
22
+
23
+ 34:'page_down',
24
+
25
+ 37:'left',
26
+ 38:'up',
27
+ 39:'right',
28
+ 40:'down',
29
+
30
+ 112:'f1',
31
+ 113:'f2',
32
+ 114:'f3',
33
+ 115:'f4',
34
+ 116:'f5',
35
+ 117:'f6',
36
+ 118:'f7',
37
+ 119:'f8',
38
+ 120:'f9',
39
+ 121:'f10',
40
+ 122:'f11',
41
+ 123:'f12',
42
+ };
43
+
44
+ _.shift_nums = {
45
+ "`":"~",
46
+ "1":"!",
47
+ "2":"@",
48
+ "3":"#",
49
+ "4":"$",
50
+ "5":"%",
51
+ "6":"^",
52
+ "7":"&",
53
+ "8":"*",
54
+ "9":"(",
55
+ "0":")",
56
+ "-":"_",
57
+ "=":"+",
58
+ ";":":",
59
+ "'":"\"",
60
+ ",":"<",
61
+ ".":">",
62
+ "/":"?",
63
+ "\\":"|"
64
+ };
65
+ _.fn.extend({
66
+ keybindings: function(bindings) {
67
+ var old = this.data("__keybindings__") || {};
68
+ if(bindings) {
69
+ return this.data("__keybindings__", _.extend(old, bindings));
70
+ }
71
+ return old;
72
+ }
73
+
74
+ ,keybind: function(binding, fn) {
75
+ var bindings = {}
76
+ ,that = this;
77
+ bindings[binding] = fn;
78
+ this.keybindings(bindings);
79
+ if(!this.data("__keybound__")) {
80
+ this.data("__keybound__", true);
81
+ this.keydown(function(e){
82
+ var bindings = that.keybindings()
83
+ ,binding
84
+ ,keys
85
+ ,modified
86
+ ,matched
87
+ ,modKeys = 'shift ctrl alt'.split(/ /)
88
+ ,key
89
+ ,requested_presses
90
+ ,presses;
91
+
92
+ if(_.special_keys[e.keyCode]) key = _.special_keys[e.keyCode];
93
+ else if(e.keyCode == 188) key=","; //If the user presses , when the type is onkeydown
94
+ else if(e.keyCode == 190) key="."; //If the user presses , when the type is onkeydown
95
+ else if(e.which != 0) key = String.fromCharCode(e.which);
96
+
97
+ for(binding in bindings) {
98
+ presses = 0;
99
+ requested_presses = binding.split('+').length;
100
+ modified = true;
101
+ _(modKeys).each(function() {
102
+ // false if the modifier is wanted, but it isn't given
103
+ if(binding.match(this) !== null) modified = e[this+"Key"];
104
+ if(e[this+"Key"]) presses++;
105
+ });
106
+ keys = binding.replace(/shift|ctrl|alt|meta/, '').split(/\++/);
107
+ matched = false;
108
+ _(keys).each(function() {
109
+ if(this !== "") {
110
+ if(this == key) {
111
+ matched = true;
112
+ presses++;
113
+ }
114
+ }
115
+ });
116
+ function execute() {
117
+ bindings[binding].call(e.target, e);
118
+ }
119
+ if(modified && matched && presses === requested_presses) {
120
+ execute();
121
+ break;
122
+ }
123
+ else {
124
+ _(keys).each(function() {
125
+ if(this !== "") {
126
+ if(this == _.shift_nums[key]) {
127
+ execute();
128
+ }
129
+ }
130
+ });
131
+ }
132
+ }
133
+ });
134
+ }
135
+ return this;
136
+ }
137
+ });
138
+ })(jQuery);