alchemy_cms 2.4.rc2 → 2.4.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +7 -4
  3. data/README.md +1 -0
  4. data/alchemy_cms.gemspec +19 -1
  5. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js +5 -2
  6. data/app/assets/javascripts/alchemy/alchemy.link_overlay.js.coffee +1 -1
  7. data/app/assets/javascripts/alchemy/alchemy.preview_window.js +4 -21
  8. data/app/assets/javascripts/alchemy/alchemy.windows.js +0 -1
  9. data/app/assets/stylesheets/alchemy/base.scss +4 -0
  10. data/app/assets/stylesheets/alchemy/elements.scss +59 -60
  11. data/app/assets/stylesheets/alchemy/flash.scss +3 -3
  12. data/app/assets/stylesheets/alchemy/form_elements.scss +15 -2
  13. data/app/assets/stylesheets/alchemy/jquery-ui.scss +11 -3
  14. data/app/assets/stylesheets/alchemy/mixins.scss +9 -0
  15. data/app/assets/stylesheets/alchemy/tinymce_content.css.scss +1 -1
  16. data/app/assets/stylesheets/alchemy/tinymce_dialog.css.scss +4 -0
  17. data/app/assets/stylesheets/alchemy/variables.scss +5 -1
  18. data/app/controllers/alchemy/admin/base_controller.rb +18 -3
  19. data/app/controllers/alchemy/admin/elements_controller.rb +4 -7
  20. data/app/controllers/alchemy/admin/pages_controller.rb +3 -4
  21. data/app/controllers/alchemy/admin/resources_controller.rb +1 -18
  22. data/app/controllers/alchemy/admin/users_controller.rb +8 -5
  23. data/app/helpers/alchemy/admin/pages_helper.rb +6 -1
  24. data/app/models/alchemy/content.rb +26 -5
  25. data/app/models/alchemy/element.rb +1 -0
  26. data/app/models/alchemy/essence_richtext.rb +1 -1
  27. data/app/models/alchemy/page.rb +131 -88
  28. data/app/views/alchemy/admin/clipboard/insert.js.erb +1 -1
  29. data/app/views/alchemy/admin/elements/create.js.erb +6 -1
  30. data/app/views/alchemy/admin/elements/trash.js.erb +1 -3
  31. data/app/views/alchemy/admin/resources/_form.html.erb +13 -1
  32. data/app/views/alchemy/admin/trash/index.html.erb +1 -1
  33. data/app/views/alchemy/base/remote_errors.js.erb +5 -1
  34. data/app/views/alchemy/essences/_essence_link_view.html.erb +2 -0
  35. data/app/views/alchemy/essences/_essence_richtext_editor.html.erb +1 -1
  36. data/config/locales/alchemy.de.yml +11 -4
  37. data/lib/alchemy/capistrano.rb +59 -1
  38. data/lib/alchemy/essence.rb +1 -0
  39. data/lib/alchemy/seeder.rb +39 -49
  40. data/lib/alchemy/tinymce.rb +1 -1
  41. data/lib/alchemy/version.rb +1 -1
  42. data/lib/rails/generators/alchemy/deploy_script/deploy_script_generator.rb +1 -1
  43. data/lib/rails/generators/alchemy/deploy_script/templates/deploy.rb.tt +3 -0
  44. data/lib/rails/generators/alchemy/elements/templates/view.html.haml +2 -2
  45. data/lib/rails/generators/alchemy/elements/templates/view.html.slim +2 -2
  46. data/lib/tasks/database.rake +25 -0
  47. data/lib/tasks/install.rake +5 -14
  48. data/spec/factories.rb +10 -0
  49. data/spec/integration/admin/resources_integration_spec.rb +64 -23
  50. data/spec/integration/pages_controller_spec.rb +0 -2
  51. data/spec/libraries/resources_helper_spec.rb +6 -2
  52. data/spec/models/content_spec.rb +31 -0
  53. data/spec/models/element_spec.rb +7 -2
  54. data/spec/models/page_spec.rb +36 -0
  55. data/vendor/assets/javascripts/jquery_plugins/jquery.dialogextend.1_0_1.js +676 -0
  56. data/vendor/assets/javascripts/jquery_plugins/jquery.ui.tabspaging.js +298 -238
  57. data/vendor/assets/javascripts/tiny_mce/langs/de.js +1 -1
  58. data/vendor/assets/javascripts/tiny_mce/langs/en.js +1 -1
  59. data/vendor/assets/javascripts/tiny_mce/license.txt +6 -6
  60. data/vendor/assets/javascripts/tiny_mce/plugins/fullscreen/editor_plugin.js +1 -1
  61. data/vendor/assets/javascripts/tiny_mce/plugins/fullscreen/fullscreen.htm +97 -97
  62. data/vendor/assets/javascripts/tiny_mce/plugins/inlinepopups/template.htm +376 -386
  63. data/vendor/assets/javascripts/tiny_mce/plugins/paste/editor_plugin.js +1 -1
  64. data/vendor/assets/javascripts/tiny_mce/plugins/paste/js/pastetext.js +30 -30
  65. data/vendor/assets/javascripts/tiny_mce/plugins/paste/js/pasteword.js +45 -45
  66. data/vendor/assets/javascripts/tiny_mce/plugins/paste/langs/de_dlg.js +1 -1
  67. data/vendor/assets/javascripts/tiny_mce/plugins/paste/pastetext.htm +17 -20
  68. data/vendor/assets/javascripts/tiny_mce/plugins/paste/pasteword.htm +12 -12
  69. data/vendor/assets/javascripts/tiny_mce/plugins/table/cell.htm +173 -183
  70. data/vendor/assets/javascripts/tiny_mce/plugins/table/css/cell.css +4 -4
  71. data/vendor/assets/javascripts/tiny_mce/plugins/table/css/row.css +7 -7
  72. data/vendor/assets/javascripts/tiny_mce/plugins/table/css/table.css +3 -3
  73. data/vendor/assets/javascripts/tiny_mce/plugins/table/editor_plugin.js +1 -1
  74. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/cell.js +280 -282
  75. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/merge_cells.js +15 -15
  76. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/row.js +221 -204
  77. data/vendor/assets/javascripts/tiny_mce/plugins/table/js/table.js +448 -435
  78. data/vendor/assets/javascripts/tiny_mce/plugins/table/langs/de_dlg.js +1 -1
  79. data/vendor/assets/javascripts/tiny_mce/plugins/table/merge_cells.htm +22 -24
  80. data/vendor/assets/javascripts/tiny_mce/plugins/table/row.htm +136 -144
  81. data/vendor/assets/javascripts/tiny_mce/plugins/table/table.htm +168 -184
  82. data/vendor/assets/javascripts/tiny_mce/themes/advanced/about.htm +46 -62
  83. data/vendor/assets/javascripts/tiny_mce/themes/advanced/anchor.htm +16 -17
  84. data/vendor/assets/javascripts/tiny_mce/themes/advanced/charmap.htm +47 -56
  85. data/vendor/assets/javascripts/tiny_mce/themes/advanced/color_picker.htm +52 -69
  86. data/vendor/assets/javascripts/tiny_mce/themes/advanced/editor_template.js +1 -852
  87. data/vendor/assets/javascripts/tiny_mce/themes/advanced/image.htm +69 -79
  88. data/vendor/assets/javascripts/tiny_mce/themes/advanced/img/icons.gif +0 -0
  89. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/about.js +48 -48
  90. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/anchor.js +50 -37
  91. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/charmap.js +317 -317
  92. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/color_picker.js +345 -331
  93. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/image.js +248 -246
  94. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/link.js +152 -146
  95. data/vendor/assets/javascripts/tiny_mce/themes/advanced/js/source_editor.js +53 -31
  96. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/de.js +1 -1
  97. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/de_dlg.js +1 -1
  98. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/en.js +1 -1
  99. data/vendor/assets/javascripts/tiny_mce/themes/advanced/langs/en_dlg.js +1 -1
  100. data/vendor/assets/javascripts/tiny_mce/themes/advanced/link.htm +46 -53
  101. data/vendor/assets/javascripts/tiny_mce/themes/advanced/shortcuts.htm +45 -57
  102. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/default/content.css +47 -182
  103. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/default/dialog.css +93 -399
  104. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/default/ui.css +191 -890
  105. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/highcontrast/content.css +24 -102
  106. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/highcontrast/dialog.css +79 -377
  107. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/highcontrast/ui.css +80 -451
  108. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/content.css +45 -167
  109. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/dialog.css +93 -399
  110. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/ui.css +194 -889
  111. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/ui_black.css +7 -33
  112. data/vendor/assets/javascripts/tiny_mce/themes/advanced/skins/o2k7/ui_silver.css +4 -15
  113. data/vendor/assets/javascripts/tiny_mce/themes/advanced/source_editor.htm +16 -19
  114. data/vendor/assets/javascripts/tiny_mce/tiny_mce.js +1 -1
  115. data/vendor/assets/javascripts/tiny_mce/tiny_mce_popup.js +1 -1
  116. data/vendor/assets/javascripts/tiny_mce/utils/editable_selects.js +46 -46
  117. data/vendor/assets/javascripts/tiny_mce/utils/form_utils.js +124 -124
  118. data/vendor/assets/javascripts/tiny_mce/utils/mctabs.js +112 -112
  119. data/vendor/assets/javascripts/tiny_mce/utils/validate.js +213 -213
  120. metadata +11 -6
  121. data/spec/dummy/public/404.html.bak +0 -26
  122. data/vendor/assets/javascripts/jquery_plugins/jquery.dialogextend.min.js +0 -8
data/spec/factories.rb CHANGED
@@ -97,4 +97,14 @@ FactoryGirl.define do
97
97
  upload_hash Time.now.hash
98
98
  end
99
99
 
100
+ factory :event do
101
+ name 'My Event'
102
+ hidden_name 'not shown'
103
+ starts_at DateTime.new(2012, 03, 02, 8, 15)
104
+ ends_at DateTime.new(2012, 03, 02, 19, 30)
105
+ description "something\nfancy"
106
+ published false
107
+ entrance_fee 12.3
108
+ end
109
+
100
110
  end
@@ -2,22 +2,12 @@ require 'spec_helper'
2
2
 
3
3
  describe "Resources" do
4
4
 
5
+ let(:event) { FactoryGirl.create(:event) }
6
+ let(:second_event) { FactoryGirl.create(:event, :name => 'My second Event', :entrance_fee => 12.32) }
7
+
5
8
  before do
6
9
  load_authorization_rules
7
10
  authorize_as_admin
8
- Event.create!(:name => 'My Event',
9
- :hidden_name => 'not shown',
10
- :starts_at => DateTime.new(2012, 03, 02, 8, 15),
11
- :ends_at => DateTime.new(2012, 03, 02, 19, 30),
12
- :description => "something\nfancy",
13
- :published => false,
14
- :entrance_fee => 12.32)
15
- Event.create!(:name => 'My second Event',
16
- :starts_at => DateTime.new(2012, 03, 02, 8, 15),
17
- :ends_at => DateTime.new(2012, 03, 02, 19, 30),
18
- :description => "something\nfancy",
19
- :published => false,
20
- :entrance_fee => 12.32)
21
11
  end
22
12
 
23
13
  describe "index view" do
@@ -28,60 +18,111 @@ describe "Resources" do
28
18
  end
29
19
 
30
20
  it "should list existing items" do
21
+ event
22
+ second_event
31
23
  visit '/admin/events'
32
24
  page.should have_content("My Event")
33
25
  page.should have_content("something fancy")
34
26
  page.should have_content("12.32")
35
27
  end
36
28
 
37
- it "should list existing resource-items nicely formatted"
29
+ it "should list existing resource-items nicely formatted" do
30
+ event
31
+ visit '/admin/events'
32
+ page.should have_selector('div#archive_all table.list')
33
+ end
38
34
 
39
35
  end
40
36
 
41
37
  describe "form for creating and updating items" do
42
- it "renders an input field according to the attribute's type"
38
+ it "renders an input field according to the attribute's type" do
39
+ visit '/admin/events/new'
40
+ page.should have_selector('input#event_name[type="text"]')
41
+ page.should have_selector('input#event_starts_at[type="date"]')
42
+ page.should have_selector('textarea#event_description')
43
+ page.should have_selector('input#event_published[type="checkbox"]')
44
+ end
43
45
  end
44
46
 
45
47
  describe "create resource item" do
46
48
 
47
49
  context "when form filled with valid data" do
48
- it "lists the new item" do
50
+ before do
49
51
  visit '/admin/events/new'
50
52
  fill_in 'event_name', :with => 'My second event'
51
53
  fill_in 'event_starts_at', :with => DateTime.new(2012, 03, 03, 20, 00)
52
54
  click_on 'Save'
55
+ end
56
+
57
+ it "lists the new item" do
53
58
  page.should have_content "My second event"
54
59
  page.should have_content "2012-03-03"
55
60
  end
61
+
62
+ it "shows a success message" do
63
+ page.should have_content("Succesfully created")
64
+ end
56
65
  end
57
66
 
58
67
  context "when form filled with invalid data" do
59
- it "shows the form again" do
68
+ before do
60
69
  visit '/admin/events/new'
61
70
  fill_in 'event_name', :with => '' #invalid!
62
71
  click_on 'Save'
63
- page.should have_selector "input#event_name"
64
72
  end
65
- it "lists invalid fields"
73
+
74
+ it "shows the form again" do
75
+ page.should have_selector "form input#event_name"
76
+ end
77
+
78
+ it "lists invalid fields" do
79
+ within('#errors') {
80
+ page.should have_content("Name can't be blank")
81
+ }
82
+ end
83
+
84
+ it "should not display success notice" do
85
+ page.should_not have_content("successfully created")
86
+ end
66
87
  end
67
88
 
68
89
  end
69
90
 
70
91
  describe "updating an item" do
71
- it "shows the updated value"
72
- end
92
+ before do
93
+ visit("/admin/events/#{event.id}/edit")
94
+ fill_in 'event_name', :with => 'New event name'
95
+ click_on 'Save'
96
+ end
97
+
98
+ it "shows the updated value" do
99
+ page.should have_content("New event name")
100
+ end
73
101
 
74
- describe "destroying an item" do
102
+ it "shows a success message" do
103
+ page.should have_content("Succesfully updated")
104
+ end
105
+ end
75
106
 
76
- it "should'n be on the list anymore", :js => true do
107
+ describe "destroying an item", :js => true do
108
+ before do
109
+ event
110
+ second_event
77
111
  visit '/admin/events'
78
112
  within('tr', :text => 'My second Event') do
79
113
  click_on 'Delete'
80
114
  end
81
115
  click_on 'Yes'
116
+ end
117
+
118
+ it "should'n be on the list anymore" do
82
119
  page.should have_content "My Event"
83
120
  page.should_not have_content "My second Event"
84
121
  end
122
+
123
+ it "should display success message" do
124
+ page.should have_content("Succesfully removed")
125
+ end
85
126
  end
86
127
 
87
128
  end
@@ -315,8 +315,6 @@ module Alchemy
315
315
  within("title") { page.should have_content("404") }
316
316
  end
317
317
 
318
- it "can be handled by matching /404 and routing it to a controller of choice when no public/404.html exists"
319
-
320
318
  end
321
319
  end
322
320
  end
@@ -1,3 +1,4 @@
1
+ require 'ostruct'
1
2
  require File.dirname(__FILE__) + "/../../lib/alchemy/resource"
2
3
  require File.dirname(__FILE__) + "/../../lib/alchemy/resources_helper"
3
4
 
@@ -103,6 +104,9 @@ describe Alchemy::ResourcesHelper do
103
104
  end
104
105
 
105
106
  describe "resource_window_size" do
106
- it "should do something"
107
+ it "should return overlay size string depending on resource attributes length" do
108
+ @controller.stub(:resource_handler).and_return(OpenStruct.new(:attributes => OpenStruct.new(:length => 4)))
109
+ @controller.resource_window_size.should == "400x240"
110
+ end
107
111
  end
108
- end
112
+ end
@@ -99,5 +99,36 @@ module Alchemy
99
99
 
100
100
  end
101
101
 
102
+ describe '.create' do
103
+ let (:element) { FactoryGirl.create(:element, :name => 'headline') }
104
+
105
+ context "with default value present" do
106
+ before do
107
+ element.stub(:content_description_for).and_return({'name' => 'headline', 'type' => 'EssenceText', 'default' => 'Welcome'})
108
+ end
109
+
110
+ it "should have the ingredient column filled with default value." do
111
+ Content.create_from_scratch(element, :name => 'headline').ingredient.should == "Welcome"
112
+ end
113
+ end
114
+ end
115
+
116
+ describe '#ingredient=' do
117
+ it "should set the given value to the ingredient column of essence" do
118
+ c = Content.create_from_scratch(element, :name => 'headline')
119
+ c.ingredient = "Welcome"
120
+ c.ingredient.should == "Welcome"
121
+ end
122
+
123
+ context "no essence associated" do
124
+ let (:element) { FactoryGirl.create(:element, :name => 'headline') }
125
+
126
+ it "should raise error" do
127
+ c = Content.create(:element_id => element.id, :name => 'headline')
128
+ expect { c.ingredient = "Welcome" }.to raise_error
129
+ end
130
+ end
131
+ end
132
+
102
133
  end
103
134
  end
@@ -263,9 +263,9 @@ module Alchemy
263
263
 
264
264
  end
265
265
 
266
- describe '#copy' do
266
+ describe '.copy' do
267
267
 
268
- let(:element) { FactoryGirl.create(:element, :create_contents_after_create => true) }
268
+ let(:element) { FactoryGirl.create(:element, :create_contents_after_create => true, :tag_list => 'red, yellow') }
269
269
 
270
270
  it "should not create contents from scratch" do
271
271
  copy = Element.copy(element)
@@ -282,6 +282,11 @@ module Alchemy
282
282
  copy.contents.collect(&:id).should_not == element.contents.collect(&:id)
283
283
  end
284
284
 
285
+ it "the copy should include source element tags" do
286
+ copy = Element.copy(element)
287
+ copy.tag_list.should == element.tag_list
288
+ end
289
+
285
290
  end
286
291
 
287
292
  describe "Finding previous or next element." do
@@ -646,5 +646,41 @@ module Alchemy
646
646
 
647
647
  end
648
648
 
649
+ describe '.copy' do
650
+ let (:page) { FactoryGirl.create(:page, :name => 'Source') }
651
+ subject { Page.copy(page) }
652
+
653
+ it "the copy should have added (copy) to name" do
654
+ subject.name.should == "#{page.name} (Copy)"
655
+ end
656
+
657
+ context "page with tags" do
658
+ before { page.tag_list = 'red, yellow'; page.save }
659
+
660
+ it "the copy should have source tag_list" do
661
+ subject.tag_list.should_not be_empty
662
+ subject.tag_list.should == page.tag_list
663
+ end
664
+ end
665
+
666
+ context "page with elements" do
667
+ before { page.elements << FactoryGirl.create(:element) }
668
+
669
+ it "the copy should have source elements" do
670
+ subject.elements.should_not be_empty
671
+ subject.elements.count.should == page.elements.count
672
+ end
673
+ end
674
+
675
+ context "page with cells" do
676
+ before { page.cells << FactoryGirl.create(:cell) }
677
+
678
+ it "the copy should have source cells" do
679
+ subject.cells.should_not be_empty
680
+ subject.cells.count.should == page.cells.length # It must be length, because!
681
+ end
682
+ end
683
+ end
684
+
649
685
  end
650
686
  end
@@ -0,0 +1,676 @@
1
+ /*!
2
+ * jQuery DialogExtend 1.0.1
3
+ *
4
+ * tvd: fixes for jQuery UI 1.9 and adjusts sizes.
5
+ *
6
+ * Copyright (c) 2010 Shum Ting Hin
7
+ *
8
+ * Licensed under MIT
9
+ * http:// www.opensource.org/licenses/mit-license.php
10
+ *
11
+ * Project Home:
12
+ * http:// code.google.com/p/jquery-dialogextend/
13
+ *
14
+ * Depends:
15
+ * jQuery 1.7.2
16
+ * jQuery UI Dialog 1.8.22
17
+ *
18
+ */
19
+ (function($){
20
+
21
+ // default settings
22
+ var defaults = {
23
+ "close" : true,
24
+ "maximize" : false,
25
+ "minimize" : false,
26
+ "dblclick" : false,
27
+ "titlebar" : false,
28
+ "icons" : {
29
+ "close" : "ui-icon-closethick",
30
+ "maximize" : "ui-icon-extlink",
31
+ "minimize" : "ui-icon-minus",
32
+ "restore" : "ui-icon-newwin"
33
+ },
34
+ "events" : {
35
+ "load" : null,
36
+ "beforeCollapse" : null,
37
+ "beforeMaximize" : null,
38
+ "beforeMinimize" : null,
39
+ "beforeRestore" : null,
40
+ "collapse" : null,
41
+ "maximize" : null,
42
+ "minimize" : null,
43
+ "restore" : null
44
+ }
45
+ };
46
+
47
+ // plugin settings (will be modified during init)
48
+ var settings;
49
+
50
+ // plubic methods
51
+ var methods = {
52
+
53
+ "init" : function( options ){
54
+ var self = this;
55
+ // validation
56
+ if ( !$(self).dialog ) {
57
+ $.error( "jQuery.dialogExtend Error : Only jQuery UI Dialog element is accepted" );
58
+ }
59
+ // merge defaults & options, without modifying the defaults
60
+ options = options || {};
61
+ options.icons = options.icons || {};
62
+ options.events = options.events || {};
63
+ settings = $.extend({}, defaults, options);
64
+ settings.icons = $.extend({}, defaults.icons, options.icons);
65
+ settings.events = $.extend({}, defaults.events, options.events);
66
+ // initiate plugin...
67
+ $(self).each(function(){
68
+ $(this)
69
+ // do bunch of things...
70
+ .dialogExtend("_verifySettings")
71
+ .dialogExtend("_initEvents")
72
+ .dialogExtend("_initStyles")
73
+ .dialogExtend("_initButtons")
74
+ .dialogExtend("_initTitleBar")
75
+ // set default dialog state
76
+ .dialogExtend("_setState", "normal")
77
+ // trigger custom event when done
78
+ .dialogExtend("_trigger", "load");
79
+ });
80
+ // maintain chainability
81
+ return self;
82
+ },
83
+
84
+ "state" : function(){
85
+ return $(this).data("dialog-extend-state");
86
+ },
87
+
88
+ "collapse" : function(){
89
+ var self = this;
90
+ // calculate new dimension
91
+ var newHeight = $(this).dialog("widget").find(".ui-dialog-titlebar").height()+15;
92
+ // start!
93
+ $(self)
94
+ // trigger custom event
95
+ .dialogExtend("_trigger", "beforeCollapse")
96
+ // remember original state
97
+ .dialogExtend("_saveSnapshot")
98
+ // modify dialog size (after hiding content)
99
+ .dialog("option", {
100
+ "resizable" : false,
101
+ "height" : newHeight,
102
+ "maxHeight" : newHeight
103
+ })
104
+ // hide content
105
+ // hide button-pane
106
+ // make title-bar no-wrap
107
+ .hide()
108
+ .dialog("widget")
109
+ .find(".ui-dialog-buttonpane:visible").hide().end()
110
+ .find(".ui-dialog-titlebar").css("white-space", "nowrap").end()
111
+ .find(".ui-dialog-content")
112
+ // mark new state
113
+ .dialogExtend("_setState", "collapsed")
114
+ // trigger custom event
115
+ .dialogExtend("_trigger", "collapse");
116
+ // maintain chainability
117
+ return self;
118
+ },
119
+
120
+ "maximize" : function(){
121
+ var self = this;
122
+ // caculate new dimension
123
+ var newHeight = $(window).height()+10;
124
+ var newWidth = $(window).width()-4;
125
+ // start!
126
+ $(self)
127
+ // trigger custom event
128
+ .dialogExtend("_trigger", "beforeMaximize")
129
+ // restore to normal state first (when necessary)
130
+ .each(function(){
131
+ if ( $(this).dialogExtend("state") != "normal" ) {
132
+ $(this).dialogExtend("_restoreWithoutTriggerEvent");
133
+ }
134
+ })
135
+ // remember original state
136
+ .dialogExtend("_saveSnapshot")
137
+ // fix dialog from scrolling
138
+ .dialog("widget")
139
+ .css({
140
+ // ie6 does not support {position:fixed} ===> simply use {absolute}
141
+ "position" : ( $.browser.msie && parseInt($.browser.version) <= 6 ) ? "absolute" : "fixed"
142
+ })
143
+ .find(".ui-dialog-content")
144
+ // show content
145
+ // show button-pane (when minimized/collapsed)
146
+ .show()
147
+ .dialog("widget")
148
+ .find(".ui-dialog-buttonpane").show().end()
149
+ .find(".ui-dialog-content")
150
+ // modify dialog with new config
151
+ .dialog("option", {
152
+ "resizable" : false,
153
+ "draggable" : false,
154
+ "height" : newHeight,
155
+ "width" : newWidth,
156
+ "position" : [1, 1]
157
+ })
158
+ // disable draggable-handle (for <titlebar=none> only)
159
+ .dialog("widget")
160
+ .find(".ui-dialog-draggable-handle").css("cursor", "text").end()
161
+ .find(".ui-dialog-content")
162
+ // mark new state
163
+ .dialogExtend("_setState", "maximized")
164
+ // modify dialog buttons according to new state
165
+ .dialogExtend("_toggleButtons")
166
+ // trigger custom event
167
+ .dialogExtend("_trigger", "maximize");
168
+ // maintain chainability
169
+ return self;
170
+ },
171
+
172
+ "minimize" : function(){
173
+ var self = this;
174
+ // caculate new dimension
175
+ var newHeight = $(this).dialog("widget").find(".ui-dialog-titlebar").height()+15;
176
+ var newWidth = 200;
177
+ // create container for (multiple) minimized dialogs (when necessary)
178
+ if ( $("#dialog-extend-fixed-container").length ) {
179
+ var fixedContainer = $("#dialog-extend-fixed-container");
180
+ } else {
181
+ var fixedContainer = $('<div id="dialog-extend-fixed-container"></div>').appendTo("body");
182
+ }
183
+ $(fixedContainer).css({
184
+ // ie6 does not support {position:fixed} ===> simply use {absolute}
185
+ "position" : ( $.browser.mise && parseInt($.browser.version) <= 6 ) ? "absolute" : "fixed",
186
+ "bottom" : 1,
187
+ "left" : 1,
188
+ "z-index" : 9999
189
+ });
190
+ // start!
191
+ $(self)
192
+ // trigger custom event
193
+ .dialogExtend("_trigger", "beforeMinimize")
194
+ // remember original state
195
+ .dialogExtend("_saveSnapshot")
196
+ // move dialog from body to container (at lower-left-hand corner)
197
+ .dialog("widget")
198
+ .css({
199
+ // float is essential for stacking dialog when there are many many minimized dialogs
200
+ "float" : "left",
201
+ "margin" : 1,
202
+ "position" : "static"
203
+ })
204
+ .appendTo(fixedContainer)
205
+ .find(".ui-dialog-content")
206
+ // modify dialog with new config
207
+ .dialog("option", {
208
+ "resizable" : false,
209
+ //"draggable" : false,
210
+ "height" : newHeight,
211
+ "width" : newWidth
212
+ })
213
+ // avoid title text overlap buttons
214
+ .dialog("widget")
215
+ .find(".ui-dialog-titlebar").each(function(){
216
+ var titlebar = this;
217
+ var buttonPane = $(this).find(".ui-dialog-titlebar-buttonpane");
218
+ var titleText = $(this).find(".ui-dialog-title");
219
+ $(titleText).css({
220
+ 'overflow': 'hidden',
221
+ 'width' : $(titlebar).width() - $(buttonPane).width() + 10
222
+ });
223
+ }).end()
224
+ .find(".ui-dialog-content")
225
+ // hide content
226
+ // hide button-pane
227
+ // make title-bar no-wrap
228
+ .hide()
229
+ .dialog("widget")
230
+ .find(".ui-dialog-buttonpane:visible").hide().end()
231
+ .find(".ui-dialog-titlebar").css("white-space", "nowrap").end()
232
+ .find(".ui-dialog-content")
233
+ // disable draggable-handle (for <titlebar=none> only)
234
+ .dialog("widget")
235
+ .draggable("option", "handle", null)
236
+ .find(".ui-dialog-draggable-handle").css("cursor", "text").end()
237
+ .find(".ui-dialog-content")
238
+ // mark new state
239
+ .dialogExtend("_setState", "minimized")
240
+ // modify dialog button according to new state
241
+ .dialogExtend("_toggleButtons")
242
+ // trigger custom event
243
+ .dialogExtend("_trigger", "minimize");
244
+ // maintain chainability
245
+ return self;
246
+ },
247
+
248
+ "restore" : function(){
249
+ var self = this;
250
+ // start!
251
+ $(self)
252
+ // trigger custom event
253
+ .dialogExtend("_trigger", "beforeRestore")
254
+ // restore to normal
255
+ .dialogExtend("_restoreWithoutTriggerEvent")
256
+ // mark new state ===> must set state *AFTER* restore because '_restoreWithoutTriggerEvent' will check 'beforeState'
257
+ .dialogExtend("_setState", "normal")
258
+ // modify dialog buttons according to new state
259
+ .dialogExtend("_toggleButtons")
260
+ // trigger custom event
261
+ .dialogExtend("_trigger", "restore");
262
+ // maintain chainability
263
+ return self;
264
+ },
265
+
266
+ "_initButtons" : function(){
267
+ var self = this;
268
+ // start operation on titlebar
269
+ var titlebar = $(self).dialog("widget").find(".ui-dialog-titlebar");
270
+ // create container for buttons
271
+ var buttonPane = $('<div class="ui-dialog-titlebar-buttonpane"></div>').appendTo(titlebar);
272
+ $(buttonPane).css({
273
+ "position" : "absolute",
274
+ "top" : "50%",
275
+ "right" : "0.3em",
276
+ "margin-top" : "-10px",
277
+ "height" : "18px"
278
+ });
279
+ // move 'close' button to button-pane
280
+ $(titlebar)
281
+ .find(".ui-dialog-titlebar-close")
282
+ // override some unwanted jquery-ui styles
283
+ .css({ "position" : "static", "top" : "auto", "right" : "auto", "margin" : 0 })
284
+ // change icon
285
+ .find(".ui-icon").removeClass("ui-icon-closethick").addClass(settings.icons.close).end()
286
+ // move to button-pane
287
+ .appendTo(buttonPane)
288
+ .end();
289
+ // append other buttons to button-pane
290
+ $(buttonPane)
291
+ .append('<a class="ui-dialog-titlebar-maximize ui-corner-all" href="#"><span class="ui-icon '+settings.icons.maximize+'">maximize</span></a>')
292
+ .append('<a class="ui-dialog-titlebar-restore ui-corner-all" href="#"><span class="ui-icon '+settings.icons.restore+'">restore</span></a>')
293
+ .append('<a class="ui-dialog-titlebar-minimize ui-corner-all" href="#"><span class="ui-icon '+settings.icons.minimize+'">minimize</span></a>')
294
+ // add effect to buttons
295
+ .find(".ui-dialog-titlebar-maximize,.ui-dialog-titlebar-minimize,.ui-dialog-titlebar-restore")
296
+ .attr("role", "button")
297
+ .mouseover(function(){ $(this).addClass("ui-state-hover"); })
298
+ .mouseout(function(){ $(this).removeClass("ui-state-hover"); })
299
+ .focus(function(){ $(this).addClass("ui-state-focus"); })
300
+ .blur(function(){ $(this).removeClass("ui-state-focus"); })
301
+ .end()
302
+ // default show buttons
303
+ // set button positions
304
+ // on-click-button
305
+ .find(".ui-dialog-titlebar-close")
306
+ .toggle(settings.close)
307
+ .end()
308
+ .find(".ui-dialog-titlebar-maximize")
309
+ .toggle(settings.maximize)
310
+ .click(function(e){
311
+ e.preventDefault();
312
+ $(self).dialogExtend("maximize");
313
+ })
314
+ .end()
315
+ .find(".ui-dialog-titlebar-minimize")
316
+ .toggle(settings.minimize)
317
+ .click(function(e){
318
+ e.preventDefault();
319
+ $(self).dialogExtend("minimize");
320
+ })
321
+ .end()
322
+ .find(".ui-dialog-titlebar-restore")
323
+ .hide()
324
+ .click(function(e){
325
+ e.preventDefault();
326
+ $(self).dialogExtend("restore");
327
+ })
328
+ .end();
329
+ // other titlebar behaviors
330
+ $(titlebar)
331
+ // on-dblclick-titlebar : maximize/minimize/collapse/restore
332
+ .dblclick(function(evt){
333
+ if ( settings.dblclick && settings.dblclick.length ) {
334
+ $(self).dialogExtend( $(self).dialogExtend("state") != "normal" ? "restore" : settings.dblclick );
335
+ }
336
+ })
337
+ // avoid text-highlight when double-click
338
+ .select(function(){
339
+ return false;
340
+ });
341
+ // maintain chainability
342
+ return self;
343
+ },
344
+
345
+ "_initEvents" : function(){
346
+ var self = this;
347
+ // bind event callbacks which specified at init
348
+ $.each(settings.events, function(type){
349
+ if ( $.isFunction( settings.events[type] ) ) {
350
+ $(self).bind(type+".dialogExtend", settings.events[type]);
351
+ }
352
+ });
353
+ // maintain chainability
354
+ return self;
355
+ },
356
+
357
+ "_initStyles" : function(){
358
+ var self = this;
359
+ // append styles for this plugin to body
360
+ if ( !$(".dialog-extend-css").length ) {
361
+ var style = '';
362
+ style += '<style class="dialog-extend-css" type="text/css">';
363
+ style += '.ui-dialog .ui-dialog-titlebar-buttonpane>a { float: right; }';
364
+ style += '.ui-dialog .ui-dialog-titlebar-maximize,';
365
+ style += '.ui-dialog .ui-dialog-titlebar-minimize,';
366
+ style += '.ui-dialog .ui-dialog-titlebar-restore { width: 19px; padding: 1px; height: 18px; }';
367
+ style += '.ui-dialog .ui-dialog-titlebar-maximize span,';
368
+ style += '.ui-dialog .ui-dialog-titlebar-minimize span,';
369
+ style += '.ui-dialog .ui-dialog-titlebar-restore span { display: block; margin: 1px; }';
370
+ style += '.ui-dialog .ui-dialog-titlebar-maximize:hover,';
371
+ style += '.ui-dialog .ui-dialog-titlebar-maximize:focus,';
372
+ style += '.ui-dialog .ui-dialog-titlebar-minimize:hover,';
373
+ style += '.ui-dialog .ui-dialog-titlebar-minimize:focus,';
374
+ style += '.ui-dialog .ui-dialog-titlebar-restore:hover,';
375
+ style += '.ui-dialog .ui-dialog-titlebar-restore:focus { padding: 0; }';
376
+ style += '.ui-dialog .ui-dialog-titlebar ::selection { background-color: transparent; }';
377
+ style += '</style>';
378
+ $(style).appendTo("body");
379
+ }
380
+ // maintain chainability
381
+ return self;
382
+ },
383
+
384
+ "_initTitleBar" : function(){
385
+ var self = this;
386
+ // modify title bar
387
+ switch ( settings.titlebar ) {
388
+ case false:
389
+ // do nothing
390
+ break;
391
+ case "none":
392
+ // create new draggable-handle as substitute of title bar
393
+ if ( $(self).dialog("option", "draggable") ) {
394
+ var handle = $("<div />").addClass("ui-dialog-draggable-handle").css("cursor", "move").height(5);
395
+ $(self).dialog("widget").prepend(handle).draggable("option", "handle", handle);
396
+ }
397
+ // remove title bar and keep it draggable
398
+ $(self)
399
+ .dialog("widget")
400
+ .find(".ui-dialog-titlebar")
401
+ // clear title text
402
+ .find(".ui-dialog-title").html("&nbsp;").end()
403
+ // keep buttons at upper-right-hand corner
404
+ .css({
405
+ "background-color" : "transparent",
406
+ "background-image" : "none",
407
+ "border" : 0,
408
+ "position" : "absolute",
409
+ "right" : 0,
410
+ "top" : 0,
411
+ "z-index" : 9999
412
+ })
413
+ .end();
414
+ break;
415
+ case "transparent":
416
+ // remove title style
417
+ $(self)
418
+ .dialog("widget")
419
+ .find(".ui-dialog-titlebar")
420
+ .css({
421
+ "background-color" : "transparent",
422
+ "background-image" : "none",
423
+ "border" : 0
424
+ });
425
+ break;
426
+ default:
427
+ $.error( "jQuery.dialogExtend Error : Invalid <titlebar> value '" + settings.titlebar + "'" );
428
+ }
429
+ // maintain chainability
430
+ return self;
431
+ },
432
+
433
+ "_loadSnapshot" : function(){
434
+ var self = this;
435
+ return {
436
+ "config" : {
437
+ "resizable" : $(self).data("original-config-resizable"),
438
+ "draggable" : $(self).data("original-config-draggable")
439
+ },
440
+ "size" : {
441
+ "height" : $(self).data("original-size-height"),
442
+ "width" : $(self).data("original-size-width"),
443
+ "maxHeight" : $(self).data("original-size-maxHeight")
444
+ },
445
+ "position" : {
446
+ "mode" : $(self).data("original-position-mode"),
447
+ "left" : $(self).data("original-position-left"),
448
+ "top" : $(self).data("original-position-top")
449
+ },
450
+ "titlebar" : {
451
+ "wrap" : $(self).data("original-titlebar-wrap")
452
+ }
453
+ };
454
+ },
455
+
456
+ "_restoreFromCollapsed" : function(){
457
+ var self = this;
458
+ var original = $(this).dialogExtend("_loadSnapshot");
459
+ // restore dialog
460
+ $(self)
461
+ // show content
462
+ // show button-pane
463
+ // fix title-bar wrap
464
+ .show()
465
+ .dialog("widget")
466
+ .find(".ui-dialog-buttonpane:hidden").show().end()
467
+ .find(".ui-dialog-titlebar").css("white-space", original.titlebar.wrap).end()
468
+ .find(".ui-dialog-content")
469
+ // restore config & size
470
+ .dialog("option", {
471
+ "resizable" : original.config.resizable,
472
+ "height" : original.size.height,
473
+ "maxHeight" : original.size.maxHeight
474
+ });
475
+ // maintain chainability
476
+ return self;
477
+ },
478
+
479
+ "_restoreFromNormal" : function(){
480
+ // do nothing actually...
481
+ // maintain chainability
482
+ return this;
483
+ },
484
+
485
+ "_restoreFromMaximized" : function(){
486
+ var self = this;
487
+ var original = $(this).dialogExtend("_loadSnapshot");
488
+ // restore dialog
489
+ $(self)
490
+ // free dialog from scrolling
491
+ // fix title-bar wrap (if dialog was minimized/collapsed)
492
+ .dialog("widget")
493
+ .css("position", original.position.mode)
494
+ .find(".ui-dialog-titlebar").css("white-space", original.titlebar.wrap).end()
495
+ .find(".ui-dialog-content")
496
+ // restore config & size
497
+ .dialog("option", {
498
+ "resizable" : original.config.resizable,
499
+ "draggable" : original.config.draggable,
500
+ "height" : original.size.height,
501
+ "width" : original.size.width,
502
+ "maxHeight" : original.size.maxHeight
503
+ })
504
+ // restore position *AFTER* size restored
505
+ .dialog("option", {
506
+ "position" : [ original.position.left, original.position.top ]
507
+ })
508
+ // restore draggable-handle (for <titlebar=none> only)
509
+ .dialog("widget")
510
+ .draggable("option", "handle", $(this).find(".ui-dialog-draggable-handle"))
511
+ .find(".ui-dialog-draggable-handle")
512
+ .css("cursor", "move");
513
+ // maintain chainability
514
+ return self;
515
+ },
516
+
517
+ "_restoreFromMinimized" : function(){
518
+ var self = this;
519
+ var original = $(this).dialogExtend("_loadSnapshot");
520
+ // restore dialog
521
+ $(self)
522
+ // move dialog back from container to body
523
+ .dialog("widget")
524
+ .appendTo("body")
525
+ .css({
526
+ "float" : "none",
527
+ "margin" : 0,
528
+ "position" : original.position.mode
529
+ })
530
+ .find(".ui-dialog-content")
531
+ // revert title text
532
+ .dialog("widget")
533
+ .find(".ui-dialog-title")
534
+ .css({ "width" : "auto" })
535
+ .end()
536
+ .find(".ui-dialog-content")
537
+ // show content
538
+ // show button-pane
539
+ // fix title-bar wrap
540
+ .show()
541
+ .dialog("widget")
542
+ .find(".ui-dialog-buttonpane:hidden").show().end()
543
+ .find(".ui-dialog-titlebar").css("white-space", original.titlebar.wrap).end()
544
+ .find(".ui-dialog-content")
545
+ // restore config & size
546
+ .dialog("option", {
547
+ "resizable" : original.config.resizable,
548
+ "draggable" : original.config.draggable,
549
+ "height" : original.size.height,
550
+ "width" : original.size.width,
551
+ "maxHeight" : original.size.maxHeight
552
+ })
553
+ // restore position *AFTER* size restored
554
+ .dialog("option", {
555
+ "position" : [ original.position.left, original.position.top ]
556
+ })
557
+ // restore draggable-handle (for <titlebar=none> only)
558
+ .dialog("widget")
559
+ .draggable("option", "handle", $(this).find(".ui-dialog-draggable-handle"))
560
+ .find(".ui-dialog-draggable-handle")
561
+ .css("cursor", "move");
562
+ // maintain chainability
563
+ return self;
564
+ },
565
+
566
+ "_restoreWithoutTriggerEvent" : function(){
567
+ var self = this;
568
+ var beforeState = $(self).dialogExtend("state");
569
+ $(self)
570
+ // restore dialog according to previous state
571
+ .dialogExtend(
572
+ beforeState == "maximized" ? "_restoreFromMaximized" :
573
+ beforeState == "minimized" ? "_restoreFromMinimized" :
574
+ beforeState == "collapsed" ? "_restoreFromCollapsed" :
575
+ beforeState == "normal" ? "_restoreFromNormal" :
576
+ $.error( "jQuery.dialogExtend Error : Cannot restore dialog from unknown state '" + beforeState +"'" )
577
+ );
578
+ // maintain chainability
579
+ return self;
580
+ },
581
+
582
+ "_saveSnapshot" : function(){
583
+ var self = this;
584
+ // remember all configs under normal state
585
+ if ( $(self).dialogExtend("state") == "normal" ) {
586
+ $(self)
587
+ .data("original-config-resizable", $(self).dialog("option", "resizable"))
588
+ .data("original-config-draggable", $(self).dialog("option", "draggable"))
589
+ .data("original-size-height", $(self).dialog("widget").height()+14)
590
+ .data("original-size-width", $(self).dialog("option", "width"))
591
+ .data("original-size-maxHeight", $(self).dialog("option", "maxHeight"))
592
+ .data("original-position-mode", $(self).dialog("widget").css("position"))
593
+ .data("original-position-left", $(self).dialog("widget").offset().left)
594
+ .data("original-position-top", $(self).dialog("widget").offset().top)
595
+ .data("original-titlebar-wrap", $(self).dialog("widget").find(".ui-dialog-titlebar").css("white-space"));
596
+ }
597
+ // maintain chainability
598
+ return self;
599
+ },
600
+
601
+ "_setState" : function(state){
602
+ var self = this;
603
+ $(self)
604
+ // toggle data state
605
+ .data("dialog-extend-state", state)
606
+ // toggle class
607
+ .removeClass("ui-dialog-normal ui-dialog-maximized ui-dialog-minimized ui-dialog-collapsed")
608
+ .addClass("ui-dialog-"+state);
609
+ // maintain chainability
610
+ return self;
611
+ },
612
+
613
+ "_toggleButtons" : function(){
614
+ var self = this;
615
+ // show or hide buttons & decide position
616
+ $(self).dialog("widget")
617
+ .find(".ui-dialog-titlebar-maximize")
618
+ .toggle( $(self).dialogExtend("state") != "maximized" && settings.maximize )
619
+ .end()
620
+ .find(".ui-dialog-titlebar-minimize")
621
+ .toggle( $(self).dialogExtend("state") != "minimized" && settings.minimize )
622
+ .end()
623
+ .find(".ui-dialog-titlebar-restore")
624
+ .toggle( $(self).dialogExtend("state") != "normal" && ( settings.maximize || settings.minimize ) )
625
+ .css({ "right" : $(self).dialogExtend("state") == "maximized" ? "1.4em" : $(self).dialogExtend("state") == "minimized" ? !settings.maximize ? "1.4em" : "2.5em" : "-9999em" })
626
+ .end();
627
+ // maintain chainability
628
+ return self;
629
+ },
630
+
631
+ "_trigger" : function( type ){
632
+ var self = this;
633
+ // trigger event with namespace when user bind to it
634
+ $(self).triggerHandler(type+".dialogExtend", this);
635
+ // maintain chainability
636
+ return self;
637
+ },
638
+
639
+ "_verifySettings" : function(){
640
+ var self = this;
641
+ // check <dblclick> option
642
+ if ( !settings.dblclick ) {
643
+ } else if ( settings.dblclick == "maximize" ) {
644
+ } else if ( settings.dblclick == "minimize" ) {
645
+ } else if ( settings.dblclick == "collapse" ) {
646
+ } else {
647
+ $.error( "jQuery.dialogExtend Error : Invalid <dblclick> value '" + settings.dblclick + "'" );
648
+ settings.dblclick = false;
649
+ }
650
+ // check <titlebar> option
651
+ if ( !settings.titlebar ) {
652
+ } else if ( settings.titlebar == "none" ) {
653
+ } else if ( settings.titlebar == "transparent" ) {
654
+ } else {
655
+ $.error( "jQuery.dialogExtend Error : Invalid <titlebar> value '" + settings.titlebar + "'" );
656
+ settings.titlebar = false;
657
+ }
658
+ // maintain chainability
659
+ return self;
660
+ }
661
+
662
+ };
663
+
664
+ // core method
665
+ $.fn.dialogExtend = function( method ){
666
+ // method calling logic
667
+ if ( methods[ method ] ) {
668
+ return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) );
669
+ } else if ( typeof method === "object" || ! method ) {
670
+ return methods.init.apply( this, arguments );
671
+ } else {
672
+ $.error( "jQuery.dialogExtend Error : Method <" + method + "> does not exist" );
673
+ }
674
+ };
675
+
676
+ }(jQuery));