dreadpiratepj-poolparty 0.0.8

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.
Files changed (109) hide show
  1. data/CHANGELOG +12 -0
  2. data/Manifest +115 -0
  3. data/README.txt +140 -0
  4. data/Rakefile +27 -0
  5. data/bin/instance +61 -0
  6. data/bin/pool +62 -0
  7. data/config/cloud_master_takeover +17 -0
  8. data/config/create_proxy_ami.sh +582 -0
  9. data/config/haproxy.conf +29 -0
  10. data/config/heartbeat.conf +8 -0
  11. data/config/heartbeat_authkeys.conf +2 -0
  12. data/config/installers/ubuntu_install.sh +77 -0
  13. data/config/monit.conf +9 -0
  14. data/config/monit/haproxy.monit.conf +7 -0
  15. data/config/monit/nginx.monit.conf +0 -0
  16. data/config/nginx.conf +24 -0
  17. data/config/reconfigure_instances_script.sh +18 -0
  18. data/config/sample-config.yml +23 -0
  19. data/config/scp_instances_script.sh +12 -0
  20. data/lib/core/array.rb +13 -0
  21. data/lib/core/exception.rb +9 -0
  22. data/lib/core/float.rb +13 -0
  23. data/lib/core/hash.rb +11 -0
  24. data/lib/core/kernel.rb +12 -0
  25. data/lib/core/module.rb +22 -0
  26. data/lib/core/object.rb +18 -0
  27. data/lib/core/proc.rb +15 -0
  28. data/lib/core/string.rb +49 -0
  29. data/lib/core/time.rb +41 -0
  30. data/lib/modules/callback.rb +133 -0
  31. data/lib/modules/ec2_wrapper.rb +82 -0
  32. data/lib/modules/safe_instance.rb +31 -0
  33. data/lib/modules/vlad_override.rb +82 -0
  34. data/lib/poolparty.rb +105 -0
  35. data/lib/poolparty/application.rb +170 -0
  36. data/lib/poolparty/init.rb +6 -0
  37. data/lib/poolparty/master.rb +329 -0
  38. data/lib/poolparty/monitors.rb +13 -0
  39. data/lib/poolparty/monitors/cpu.rb +19 -0
  40. data/lib/poolparty/monitors/memory.rb +26 -0
  41. data/lib/poolparty/monitors/web.rb +23 -0
  42. data/lib/poolparty/optioner.rb +16 -0
  43. data/lib/poolparty/plugin.rb +43 -0
  44. data/lib/poolparty/plugin_manager.rb +67 -0
  45. data/lib/poolparty/provider.rb +2 -0
  46. data/lib/poolparty/provider/packages/essential.rb +6 -0
  47. data/lib/poolparty/provider/packages/git.rb +4 -0
  48. data/lib/poolparty/provider/packages/haproxy.rb +20 -0
  49. data/lib/poolparty/provider/packages/heartbeat.rb +4 -0
  50. data/lib/poolparty/provider/packages/monit.rb +6 -0
  51. data/lib/poolparty/provider/packages/rsync.rb +4 -0
  52. data/lib/poolparty/provider/packages/ruby.rb +37 -0
  53. data/lib/poolparty/provider/packages/s3fuse.rb +11 -0
  54. data/lib/poolparty/provider/provider.rb +60 -0
  55. data/lib/poolparty/remote_instance.rb +216 -0
  56. data/lib/poolparty/remoter.rb +106 -0
  57. data/lib/poolparty/remoting.rb +112 -0
  58. data/lib/poolparty/scheduler.rb +103 -0
  59. data/lib/poolparty/tasks.rb +29 -0
  60. data/lib/poolparty/tasks/cloud.rake +57 -0
  61. data/lib/poolparty/tasks/development.rake +38 -0
  62. data/lib/poolparty/tasks/ec2.rake +20 -0
  63. data/lib/poolparty/tasks/instance.rake +63 -0
  64. data/lib/poolparty/tasks/plugins.rake +30 -0
  65. data/lib/poolparty/tasks/server.rake +42 -0
  66. data/lib/poolparty/tmp.rb +46 -0
  67. data/lib/s3/s3_object_store_folders.rb +44 -0
  68. data/misc/basics_tutorial.txt +142 -0
  69. data/poolparty.gemspec +72 -0
  70. data/spec/application_spec.rb +39 -0
  71. data/spec/callback_spec.rb +194 -0
  72. data/spec/core_spec.rb +15 -0
  73. data/spec/helpers/ec2_mock.rb +44 -0
  74. data/spec/kernel_spec.rb +11 -0
  75. data/spec/master_spec.rb +203 -0
  76. data/spec/monitors/cpu_monitor_spec.rb +38 -0
  77. data/spec/monitors/memory_spec.rb +50 -0
  78. data/spec/monitors/misc_monitor_spec.rb +50 -0
  79. data/spec/monitors/web_spec.rb +39 -0
  80. data/spec/optioner_spec.rb +22 -0
  81. data/spec/plugin_manager_spec.rb +31 -0
  82. data/spec/plugin_spec.rb +101 -0
  83. data/spec/pool_binary_spec.rb +10 -0
  84. data/spec/poolparty_spec.rb +15 -0
  85. data/spec/provider_spec.rb +17 -0
  86. data/spec/remote_instance_spec.rb +149 -0
  87. data/spec/remoter_spec.rb +65 -0
  88. data/spec/remoting_spec.rb +84 -0
  89. data/spec/scheduler_spec.rb +75 -0
  90. data/spec/spec_helper.rb +39 -0
  91. data/spec/string_spec.rb +28 -0
  92. data/web/static/conf/nginx.conf +22 -0
  93. data/web/static/site/images/balloon.png +0 -0
  94. data/web/static/site/images/cb.png +0 -0
  95. data/web/static/site/images/clouds.png +0 -0
  96. data/web/static/site/images/railsconf_preso_img.png +0 -0
  97. data/web/static/site/index.html +71 -0
  98. data/web/static/site/javascripts/application.js +3 -0
  99. data/web/static/site/javascripts/corner.js +178 -0
  100. data/web/static/site/javascripts/jquery-1.2.6.pack.js +11 -0
  101. data/web/static/site/misc.html +42 -0
  102. data/web/static/site/storage/pool_party_presentation.pdf +0 -0
  103. data/web/static/site/stylesheets/application.css +100 -0
  104. data/web/static/site/stylesheets/reset.css +17 -0
  105. data/web/static/src/layouts/application.haml +25 -0
  106. data/web/static/src/pages/index.haml +25 -0
  107. data/web/static/src/pages/misc.haml +5 -0
  108. data/web/static/src/stylesheets/application.sass +100 -0
  109. metadata +260 -0
@@ -0,0 +1,65 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class TestRemote
4
+ include Remoter
5
+ include Callbacks
6
+ attr_accessor :ip
7
+ end
8
+ describe "Remoter" do
9
+ before(:each) do
10
+ @instance = RemoteInstance.new
11
+ @master = Master.new
12
+
13
+ @remoter = TestRemote.new
14
+ @remoter.stub!(:put).and_return "true"
15
+ # @tempfile = Tempfile.new("/tmp") do |f|
16
+ # f << "hi"
17
+ # end
18
+ Application.stub!(:ec2_dir).and_return "/Users"
19
+ Application.stub!(:keypair).and_return "app"
20
+ Application.stub!(:username).and_return "root"
21
+ end
22
+ it "should have an ssh method that corresponds to ssm with the keypair" do
23
+ @remoter.ssh_string.should == "ssh -i /Users/id_rsa-app -o StrictHostKeyChecking=no"
24
+ end
25
+ it "should have a list of ssh_tasks" do
26
+ @remoter.ssh_tasks.should == []
27
+ end
28
+ it "should have a list of scp_tasks" do
29
+ @remoter.scp_tasks.should == []
30
+ end
31
+ it "should reset the values to nil when calling reset" do
32
+ @remoter.target_hosts.should_not be_nil
33
+ @remoter.reset!
34
+ @hosts.should be_nil
35
+ end
36
+ describe "executing" do
37
+ it "should call set_hosts before it executes the tasks" do
38
+ @remoter.should_receive(:set_hosts).once
39
+ @remoter.execute_tasks {}
40
+ end
41
+ it "should not call set_hosts before it executes the task if it explicitly doesn't want it to" do
42
+ @remoter.should_not_receive(:set_hosts)
43
+ @remoter.execute_tasks(:dont_set_hosts => true) {}
44
+ end
45
+
46
+ describe "ssh" do
47
+ before(:each) do
48
+ @arr = []
49
+ @arr << @a = proc{puts "hello"}
50
+ @arr << @b = proc{puts "world"}
51
+ @instance.stub!(:ip).and_return("127.0.0.1")
52
+ end
53
+ it "should run the tasks in an array with run_thread_list" do
54
+ @remoter.should_receive(:run_thread_list).once
55
+ @remoter.run_array_of_tasks(@arr)
56
+ end
57
+
58
+ it "should be able to collect the list of the target hosts's ips" do
59
+ Master.should_receive(:new).and_return(@master)
60
+ @master.stub!(:nodes).and_return([@instance])
61
+ @remoter.target_hosts.should == %w(127.0.0.1)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,84 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + "/helpers/ec2_mock"
3
+
4
+ describe "Master remoting: " do
5
+ before(:each) do
6
+ Kernel.stub!(:system).and_return true
7
+ Application.stub!(:environment).and_return("test") # So it doesn't daemonize
8
+ Application.stub!(:minimum_instances).and_return(2)
9
+ Application.stub!(:maximum_instances).and_return(10)
10
+ Application.stub!(:polling_time).and_return(0.1)
11
+ Application.stub!(:verbose).and_return(false) # Turn off messaging
12
+
13
+ @master = Master.new
14
+ @master.launch_new_instance!
15
+ end
16
+ describe "starting" do
17
+ before(:each) do
18
+ @master.start_cloud!
19
+ end
20
+ it "should start the cloud with instances" do
21
+ @master.list_of_instances.should_not be_empty
22
+ end
23
+ it "should start the cloud with running instances" do
24
+ @master.list_of_running_instances.should_not be_empty
25
+ end
26
+ it "should start with the minimum_instances running" do
27
+ wait 0.5 # Give the last one time to get to running
28
+ @master.list_of_running_instances.size.should == Application.minimum_instances
29
+ end
30
+ end
31
+ describe "maintaining" do
32
+ it "should maintain the minimum_instances if one goes down" do
33
+ @master.start_cloud!
34
+ wait 0.2 # Give the two instances time to boot up
35
+ (Application.minimum_instances - @master.number_of_pending_and_running_instances).should == 0
36
+
37
+ # Kill one off to test how it handles the response
38
+ @master.terminate_instance!(@master.list_of_running_instances[0][:instance_id])
39
+ (Application.minimum_instances - @master.number_of_pending_and_running_instances).should == 1
40
+ @master.launch_minimum_instances # Assume this runs in the bg process
41
+
42
+ (Application.minimum_instances - @master.number_of_pending_and_running_instances).should == 0
43
+ @master.number_of_pending_and_running_instances.should == Application.minimum_instances
44
+ end
45
+ it "should launch a new instance when the load gets too heavy set in the configs" do
46
+ @master.stub!(:expand?).and_return true
47
+ @master.start_cloud!
48
+ wait 0.2 # Give the two instances time to boot up
49
+ (Application.minimum_instances - @master.number_of_pending_and_running_instances).should == 0
50
+ @master.scale_cloud!
51
+ @master.nodes.size.should == Application.minimum_instances + 1
52
+ end
53
+ it "should terminate an instance when the load shows that it's too light" do
54
+ @master.stub!(:contract?).and_return true
55
+ @master.start_cloud!
56
+ @master.request_launch_new_instance
57
+ wait 0.5 # Give the two instances time to boot up
58
+ @master.number_of_pending_and_running_instances.should == Application.minimum_instances + 1
59
+ @master.scale_cloud!
60
+ @master.number_of_pending_and_running_instances.should == Application.minimum_instances
61
+ end
62
+ end
63
+ describe "configuring" do
64
+ it "should call configure on all of the nodes when calling reconfigure_running_instances" do
65
+ @master.nodes.each {|a|
66
+ a.stub!(:status).and_return("running")
67
+ a.should_receive(:configure).and_return true
68
+ }
69
+ @master.reconfigure_running_instances
70
+ end
71
+ it "should call restart_with_monit on all of the nodes when calling restart_running_instances_services" do
72
+ @master.nodes.each {|a| a.should_receive(:restart_with_monit).and_return true }
73
+ @master.restart_running_instances_services
74
+ end
75
+ it "should be able to say there are no number_of_unconfigured_nodes left when all the nodes are configured" do
76
+ @master.nodes.each {|a| a.should_receive(:stack_installed?).and_return true }
77
+ @master.number_of_unconfigured_nodes.should == 0
78
+ end
79
+ it "should be able to say that there is an unconfigured node" do
80
+ @master.nodes[-1].should_receive(:stack_installed?).and_return false
81
+ @master.number_of_unconfigured_nodes.should_not == 0
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,75 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class TestSched
4
+ include Scheduler
5
+ end
6
+ describe "Scheduler" do
7
+ before(:each) do
8
+ @test = TestSched.new
9
+ end
10
+ it "should create a ScheduleTasks" do
11
+ @test._tasker.class.should == ScheduleTasks
12
+ end
13
+ describe "run_thread_loop" do
14
+ before(:each) do
15
+ @klass = Class.new
16
+ @klass.stub!(:pop).once.and_return true
17
+ @block = Proc.new {@klass.pop}
18
+ end
19
+ it "should yield the block that it is given" do
20
+ @test.run_thread_list &@block
21
+ end
22
+ it "should run run_threads" do
23
+ @test.should_receive(:run_threads).and_return true
24
+ @test.run_thread_list &@block
25
+ end
26
+ it "should call run on the _tasker" do
27
+ @test._tasker.should_receive(:run).once.and_return true
28
+ @test.run_thread_list &@block
29
+ end
30
+ describe "ScheduleTasks class" do
31
+ before(:each) do
32
+ @stasks = ScheduleTasks.new
33
+ @test.stub!(:_tasker).and_return(@stasks)
34
+ end
35
+ it "should have its tasks listed as an empty array if there are no tasks added" do
36
+ @stasks.tasks.should == []
37
+ end
38
+ it "should have one task listed if it is added" do
39
+ @test.add_task {@klass.pop}
40
+ @stasks.tasks.size.should == 1
41
+ end
42
+ it "should add each task as a thread" do
43
+ Thread.should_receive(:new).once
44
+ @stasks.push proc{puts "hi"}
45
+ end
46
+ end
47
+ describe "when running" do
48
+ before(:each) do
49
+ @test.add_task {@klass.pop}
50
+ end
51
+ it "should not run the tasks after adding them" do
52
+ @klass.should_not_receive(:pop)
53
+ @test.add_task {@klass.pop}
54
+ end
55
+ it "should run the tasks when run_thread_list" do
56
+ @klass.should_receive(:pop)
57
+ @test.run_thread_list
58
+ end
59
+ it "should be able to add tasks and have the run_thread_list run" do
60
+ @test._tasker.class.should_receive(:synchronize).once
61
+ @test.run_thread_list
62
+ end
63
+ it "should empty all the tasks after running them in the loop" do
64
+ @test.run_thread_list
65
+ @test._tasker.tasks.size.should == 0
66
+ end
67
+ describe "daemonizing" do
68
+ it "should detached the process" do
69
+ Process.should_receive(:detach).once
70
+ @test.daemonize
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,39 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. lib poolparty])
2
+
3
+ %w(test/spec).each do |library|
4
+ begin
5
+ require library
6
+ rescue
7
+ STDERR.puts "== Cannot run test without #{library}"
8
+ end
9
+ end
10
+
11
+ Dir["#{File.dirname(__FILE__)}/helpers/**"].each {|a| require a}
12
+
13
+ include PoolParty
14
+ extend PoolParty
15
+
16
+ Application.environment = "test"
17
+ Application.verbose = false
18
+
19
+ def wait_launch(time=5)
20
+ pid = fork {yield}
21
+ wait time
22
+ Process.kill("INT", pid)
23
+ Process.wait(pid, 0)
24
+ end
25
+
26
+ module Test::Unit::AssertDifference
27
+ def assert_difference(object, method = nil, difference = 1)
28
+ initial_value = object.send(method)
29
+ yield
30
+ assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
31
+ end
32
+
33
+ def assert_no_difference(object, method, &block)
34
+ assert_difference object, method, 0, &block
35
+ end
36
+ end
37
+
38
+ Test::Spec::Should.send(:include, Test::Unit::AssertDifference)
39
+ Test::Spec::ShouldNot.send(:include, Test::Unit::AssertDifference)
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "String" do
4
+ before(:each) do
5
+ @string = "string"
6
+ @string.stub!(:bucket_objects).and_return([])
7
+ end
8
+ # Dumb test
9
+ it "should be able to call bucket_objects on itself" do
10
+ @string.should_receive(:bucket_objects)
11
+ @string.bucket_objects
12
+ end
13
+ describe "with config replacements" do
14
+ it "should replace those syms in the string" do
15
+ ("new :port" ^ {:port => 100}).should == "new 100"
16
+ end
17
+ it "should be able to detect vars" do
18
+ @string=<<-EOC
19
+ listen web_proxy 127.0.0.1::client_port
20
+ \tserver web1 127.0.0.1::port weight 1 minconn 3 maxconn 6 check inter 30000
21
+ EOC
22
+ (@string ^ {:client_port => 3000, :port => 3001}).should ==<<-EOO
23
+ listen web_proxy 127.0.0.1:3000
24
+ \tserver web1 127.0.0.1:3001 weight 1 minconn 3 maxconn 6 check inter 30000
25
+ EOO
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ server {
2
+
3
+ listen 80;
4
+ server_name www.poolpartyrb.com;
5
+ rewrite ^/(.*) http://poolpartyrb.com permanent;
6
+ }
7
+
8
+
9
+ server {
10
+
11
+ listen 80;
12
+ server_name poolpartyrb.com;
13
+
14
+ access_log /apps/poolpartyrb/logs/access.log;
15
+ error_log /apps/poolpartyrb/logs/error.log;
16
+
17
+ location / {
18
+ root /apps/poolpartyrb;
19
+ index index.html;
20
+ }
21
+
22
+ }
Binary file
Binary file
Binary file
@@ -0,0 +1,71 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2
+ <html>
3
+ <head>
4
+ <title>PoolParty - Cloud EC2 Computing Made Easy</title>
5
+ <link href="/stylesheets/reset.css" media="all" rel="stylesheet" type="text/css"/>
6
+ <link href="/stylesheets/application.css" media="all" rel="stylesheet" type="text/css"/>
7
+ <script type="text/javascript" src="/javascripts/jquery-1.2.6.pack.js"></script>
8
+ <script type="text/javascript" src="/javascripts/corner.js"></script>
9
+ <script type="text/javascript" src="/javascripts/application.js"></script>
10
+ </head>
11
+ <body>
12
+ <div class='container'>
13
+ <div class='balloon'><img alt="Balloon" src="images/balloon.png"/></div>
14
+ <div class='header'>
15
+ <h1><a href="/">PoolParty</a></h1>
16
+ <div class='clouds'><img alt="Clouds" src="images/clouds.png"/></div>
17
+ <div class='links'>
18
+ <a href="#">api</a>
19
+ <a href="http://poolparty.rubyforge.org/">docs</a>
20
+ <a href="misc.html">misc</a>
21
+ <a href="#">group</a>
22
+ </div>
23
+ <div class='clear'>
24
+ </div>
25
+ </div>
26
+ <div class='clear'>
27
+ </div>
28
+ <div class='content'>
29
+ <h2>
30
+ One Ruby <strong><a href="http://github.com/auser/pool-party/tree/master">Gem</a></strong> = easy EC2 computing cloud.
31
+ </h2>
32
+ <div class='tout'>
33
+ <p class='alt'>
34
+ Amazon's Elastic Compute Cloud provides pay-per-use scalability to the masses. However, clouds do not have innate load-balancing ability, and there is no native failover solution.
35
+ </p>
36
+ <p class='alt'>
37
+ PoolParty is an <strong>open source</strong> tool that <strong>automates</strong> deployment, monitoring, and load balancing of EC2 instances. Configure once, and relax by the poolside -- PoolParty will keep your site and its instances afloat.
38
+ </p>
39
+ </div>
40
+ <p>
41
+ "I'm convinced, how do I get started?"
42
+ <code>
43
+ sudo gem install auser-poolparty -s http://gems.github.com
44
+ </code>
45
+ </p>
46
+ <p>
47
+ "Okay, are there examples or documentation?"
48
+ <span class='alt'>
49
+ <a href="http://poolparty.rubyforge.org/">Docs</a>
50
+ &
51
+ <a href="#">Examples</a>
52
+ </span>
53
+ </p>
54
+ <p>
55
+ "How do I extend it?"
56
+ <p class='alt'>
57
+ More coming soon
58
+ <strong>
59
+ <a href='http://github.com/auser/pool-party-plugins/tree/master'>
60
+ http://github.com/auser/pool-party-plugins/tree/master
61
+ </a>
62
+ </strong>
63
+ </p>
64
+ </p>
65
+ </div>
66
+ <div class='cb'>
67
+ <a href="http://www.citrusbyte.com"><img alt="Cb" src="images/cb.png"/></a>
68
+ </div>
69
+ </div>
70
+ </body>
71
+ </html>
@@ -0,0 +1,3 @@
1
+ $(document).ready(function() {
2
+ $(".content").corner();
3
+ });
@@ -0,0 +1,178 @@
1
+ /*
2
+ * jQuery corner plugin
3
+ *
4
+ * version 1.92 (12/18/2007)
5
+ *
6
+ * Dual licensed under the MIT and GPL licenses:
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ * http://www.gnu.org/licenses/gpl.html
9
+ */
10
+
11
+ /**
12
+ * The corner() method provides a simple way of styling DOM elements.
13
+ *
14
+ * corner() takes a single string argument: $().corner("effect corners width")
15
+ *
16
+ * effect: The name of the effect to apply, such as round or bevel.
17
+ * If you don't specify an effect, rounding is used.
18
+ *
19
+ * corners: The corners can be one or more of top, bottom, tr, tl, br, or bl.
20
+ * By default, all four corners are adorned.
21
+ *
22
+ * width: The width specifies the width of the effect; in the case of rounded corners this
23
+ * will be the radius of the width.
24
+ * Specify this value using the px suffix such as 10px, and yes it must be pixels.
25
+ *
26
+ * For more details see: http://methvin.com/jquery/jq-corner.html
27
+ * For a full demo see: http://malsup.com/jquery/corner/
28
+ *
29
+ *
30
+ * @example $('.adorn').corner();
31
+ * @desc Create round, 10px corners
32
+ *
33
+ * @example $('.adorn').corner("25px");
34
+ * @desc Create round, 25px corners
35
+ *
36
+ * @example $('.adorn').corner("notch bottom");
37
+ * @desc Create notched, 10px corners on bottom only
38
+ *
39
+ * @example $('.adorn').corner("tr dog 25px");
40
+ * @desc Create dogeared, 25px corner on the top-right corner only
41
+ *
42
+ * @example $('.adorn').corner("round 8px").parent().css('padding', '4px').corner("round 10px");
43
+ * @desc Create a rounded border effect by styling both the element and its parent
44
+ *
45
+ * @name corner
46
+ * @type jQuery
47
+ * @param String options Options which control the corner style
48
+ * @cat Plugins/Corner
49
+ * @return jQuery
50
+ * @author Dave Methvin (dave.methvin@gmail.com)
51
+ * @author Mike Alsup (malsup@gmail.com)
52
+ */
53
+ (function($) {
54
+
55
+ $.fn.corner = function(o) {
56
+ var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent);
57
+ function sz(el, p) { return parseInt($.css(el,p))||0; };
58
+ function hex2(s) {
59
+ var s = parseInt(s).toString(16);
60
+ return ( s.length < 2 ) ? '0'+s : s;
61
+ };
62
+ function gpc(node) {
63
+ for ( ; node && node.nodeName.toLowerCase() != 'html'; node = node.parentNode ) {
64
+ var v = $.css(node,'backgroundColor');
65
+ if ( v.indexOf('rgb') >= 0 ) {
66
+ if ($.browser.safari && v == 'rgba(0, 0, 0, 0)')
67
+ continue;
68
+ var rgb = v.match(/\d+/g);
69
+ return '#'+ hex2(rgb[0]) + hex2(rgb[1]) + hex2(rgb[2]);
70
+ }
71
+ if ( v && v != 'transparent' )
72
+ return v;
73
+ }
74
+ return '#ffffff';
75
+ };
76
+ function getW(i) {
77
+ switch(fx) {
78
+ case 'round': return Math.round(width*(1-Math.cos(Math.asin(i/width))));
79
+ case 'cool': return Math.round(width*(1+Math.cos(Math.asin(i/width))));
80
+ case 'sharp': return Math.round(width*(1-Math.cos(Math.acos(i/width))));
81
+ case 'bite': return Math.round(width*(Math.cos(Math.asin((width-i-1)/width))));
82
+ case 'slide': return Math.round(width*(Math.atan2(i,width/i)));
83
+ case 'jut': return Math.round(width*(Math.atan2(width,(width-i-1))));
84
+ case 'curl': return Math.round(width*(Math.atan(i)));
85
+ case 'tear': return Math.round(width*(Math.cos(i)));
86
+ case 'wicked': return Math.round(width*(Math.tan(i)));
87
+ case 'long': return Math.round(width*(Math.sqrt(i)));
88
+ case 'sculpt': return Math.round(width*(Math.log((width-i-1),width)));
89
+ case 'dog': return (i&1) ? (i+1) : width;
90
+ case 'dog2': return (i&2) ? (i+1) : width;
91
+ case 'dog3': return (i&3) ? (i+1) : width;
92
+ case 'fray': return (i%2)*width;
93
+ case 'notch': return width;
94
+ case 'bevel': return i+1;
95
+ }
96
+ };
97
+ o = (o||"").toLowerCase();
98
+ var keep = /keep/.test(o); // keep borders?
99
+ var cc = ((o.match(/cc:(#[0-9a-f]+)/)||[])[1]); // corner color
100
+ var sc = ((o.match(/sc:(#[0-9a-f]+)/)||[])[1]); // strip color
101
+ var width = parseInt((o.match(/(\d+)px/)||[])[1]) || 10; // corner width
102
+ var re = /round|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dog/;
103
+ var fx = ((o.match(re)||['round'])[0]);
104
+ var edges = { T:0, B:1 };
105
+ var opts = {
106
+ TL: /top|tl/.test(o), TR: /top|tr/.test(o),
107
+ BL: /bottom|bl/.test(o), BR: /bottom|br/.test(o)
108
+ };
109
+ if ( !opts.TL && !opts.TR && !opts.BL && !opts.BR )
110
+ opts = { TL:1, TR:1, BL:1, BR:1 };
111
+ var strip = document.createElement('div');
112
+ strip.style.overflow = 'hidden';
113
+ strip.style.height = '1px';
114
+ strip.style.backgroundColor = sc || 'transparent';
115
+ strip.style.borderStyle = 'solid';
116
+ return this.each(function(index){
117
+ var pad = {
118
+ T: parseInt($.css(this,'paddingTop'))||0, R: parseInt($.css(this,'paddingRight'))||0,
119
+ B: parseInt($.css(this,'paddingBottom'))||0, L: parseInt($.css(this,'paddingLeft'))||0
120
+ };
121
+
122
+ if ($.browser.msie) this.style.zoom = 1; // force 'hasLayout' in IE
123
+ if (!keep) this.style.border = 'none';
124
+ strip.style.borderColor = cc || gpc(this.parentNode);
125
+ var cssHeight = $.curCSS(this, 'height');
126
+
127
+ for (var j in edges) {
128
+ var bot = edges[j];
129
+ // only add stips if needed
130
+ if ((bot && (opts.BL || opts.BR)) || (!bot && (opts.TL || opts.TR))) {
131
+ strip.style.borderStyle = 'none '+(opts[j+'R']?'solid':'none')+' none '+(opts[j+'L']?'solid':'none');
132
+ var d = document.createElement('div');
133
+ $(d).addClass('jquery-corner');
134
+ var ds = d.style;
135
+
136
+ bot ? this.appendChild(d) : this.insertBefore(d, this.firstChild);
137
+
138
+ if (bot && cssHeight != 'auto') {
139
+ if ($.css(this,'position') == 'static')
140
+ this.style.position = 'relative';
141
+ ds.position = 'absolute';
142
+ ds.bottom = ds.left = ds.padding = ds.margin = '0';
143
+ if ($.browser.msie)
144
+ ds.setExpression('width', 'this.parentNode.offsetWidth');
145
+ else
146
+ ds.width = '100%';
147
+ }
148
+ else if (!bot && $.browser.msie) {
149
+ if ($.css(this,'position') == 'static')
150
+ this.style.position = 'relative';
151
+ ds.position = 'absolute';
152
+ ds.top = ds.left = ds.right = ds.padding = ds.margin = '0';
153
+
154
+ // fix ie6 problem when blocked element has a border width
155
+ var bw = 0;
156
+ if (ie6 || !$.boxModel)
157
+ bw = sz(this,'borderLeftWidth') + sz(this,'borderRightWidth');
158
+ ie6 ? ds.setExpression('width', 'this.parentNode.offsetWidth - '+bw+'+ "px"') : ds.width = '100%';
159
+ }
160
+ else {
161
+ ds.margin = !bot ? '-'+pad.T+'px -'+pad.R+'px '+(pad.T-width)+'px -'+pad.L+'px' :
162
+ (pad.B-width)+'px -'+pad.R+'px -'+pad.B+'px -'+pad.L+'px';
163
+ }
164
+
165
+ for (var i=0; i < width; i++) {
166
+ var w = Math.max(0,getW(i));
167
+ var e = strip.cloneNode(false);
168
+ e.style.borderWidth = '0 '+(opts[j+'R']?w:0)+'px 0 '+(opts[j+'L']?w:0)+'px';
169
+ bot ? d.appendChild(e) : d.insertBefore(e, d.firstChild);
170
+ }
171
+ }
172
+ }
173
+ });
174
+ };
175
+
176
+ $.fn.uncorner = function(o) { return $('.jquery-corner', this).remove(); };
177
+
178
+ })(jQuery);