zen 0.4 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/.aspell.en.pws +31 -0
  2. data/.gems +1 -1
  3. data/.mailmap +1 -0
  4. data/.travis.yml +13 -7
  5. data/.yardopts +1 -1
  6. data/Rakefile +2 -2
  7. data/guide/changelog.md +23 -5
  8. data/guide/css/common.css +48 -0
  9. data/guide/javascript.md +1 -0
  10. data/guide/javascript/zen_form.md +46 -0
  11. data/guide/zen_compared.md +104 -0
  12. data/lib/zen.rb +21 -12
  13. data/lib/zen/helper/breadcrumb.rb +1 -1
  14. data/lib/zen/helper/message.rb +4 -5
  15. data/lib/zen/language.rb +75 -10
  16. data/lib/zen/markup.rb +20 -8
  17. data/lib/zen/model/init.rb +14 -9
  18. data/lib/zen/model/plugin/events.rb +1 -1
  19. data/lib/zen/package.rb +71 -14
  20. data/lib/zen/package/categories/lib/categories/controller/categories.rb +39 -12
  21. data/lib/zen/package/categories/lib/categories/controller/category_groups.rb +26 -5
  22. data/lib/zen/package/categories/lib/categories/helper/category.rb +37 -6
  23. data/lib/zen/package/categories/lib/categories/helper/category_frontend.rb +2 -2
  24. data/lib/zen/package/categories/lib/categories/view/admin/categories/form.xhtml +26 -29
  25. data/lib/zen/package/comments/lib/comments.rb +9 -1
  26. data/lib/zen/package/comments/lib/comments/anti_spam.rb +1 -1
  27. data/lib/zen/package/comments/lib/comments/controller/comments.rb +59 -14
  28. data/lib/zen/package/comments/lib/comments/controller/comments_form.rb +49 -11
  29. data/lib/zen/package/comments/lib/comments/helper/comment.rb +14 -2
  30. data/lib/zen/package/comments/lib/comments/view/admin/comments/form.xhtml +3 -3
  31. data/lib/zen/package/comments/migrations/1308774099_comment_status.rb +1 -1
  32. data/lib/zen/package/custom_fields/lib/custom_fields/blue_form_parameters.rb +12 -0
  33. data/lib/zen/package/custom_fields/lib/custom_fields/controller/custom_field_groups.rb +24 -6
  34. data/lib/zen/package/custom_fields/lib/custom_fields/controller/custom_field_types.rb +68 -20
  35. data/lib/zen/package/custom_fields/lib/custom_fields/controller/custom_fields.rb +106 -26
  36. data/lib/zen/package/custom_fields/lib/custom_fields/helper/custom_field.rb +50 -11
  37. data/lib/zen/package/custom_fields/lib/custom_fields/view/admin/custom-fields/form.xhtml +2 -2
  38. data/lib/zen/package/dashboard/lib/dashboard/controller/dashboard.rb +1 -1
  39. data/lib/zen/package/menu.rb +6 -1
  40. data/lib/zen/package/menus/lib/menus/controller/menu_items.rb +44 -9
  41. data/lib/zen/package/menus/lib/menus/controller/menus.rb +53 -13
  42. data/lib/zen/package/menus/lib/menus/helper/menu.rb +30 -4
  43. data/lib/zen/package/menus/lib/menus/model/menu.rb +4 -2
  44. data/lib/zen/package/sections/lib/sections/controller/section_entries.rb +48 -9
  45. data/lib/zen/package/sections/lib/sections/controller/sections.rb +77 -21
  46. data/lib/zen/package/sections/lib/sections/helper/section.rb +32 -4
  47. data/lib/zen/package/sections/lib/sections/model/section_entry.rb +1 -1
  48. data/lib/zen/package/sections/lib/sections/view/admin/section-entries/form.xhtml +5 -4
  49. data/lib/zen/package/sections/lib/sections/view/admin/sections/form.xhtml +3 -3
  50. data/lib/zen/package/sections/migrations/1308813320_section_entry_statuses.rb +1 -1
  51. data/lib/zen/package/settings/lib/settings/blue_form_parameters.rb +2 -2
  52. data/lib/zen/package/settings/lib/settings/controller/settings.rb +60 -15
  53. data/lib/zen/package/settings/lib/settings/view/admin/settings/index.xhtml +1 -1
  54. data/lib/zen/package/users/lib/users/controller/user_groups.rb +42 -7
  55. data/lib/zen/package/users/lib/users/controller/users.rb +78 -16
  56. data/lib/zen/package/users/lib/users/helper/users.rb +29 -4
  57. data/lib/zen/package/users/lib/users/view/admin/user-groups/form.xhtml +2 -2
  58. data/lib/zen/package/users/lib/users/view/admin/users/form.xhtml +2 -2
  59. data/lib/zen/public/admin/zen/css/general.css +5 -0
  60. data/lib/zen/public/admin/zen/css/messages.css +1 -0
  61. data/lib/zen/public/admin/zen/css/tables.css +33 -0
  62. data/lib/zen/public/admin/zen/css/tabs.css +8 -0
  63. data/lib/zen/public/admin/zen/js/index.js +21 -2
  64. data/lib/zen/public/admin/zen/js/lib/events.js +45 -0
  65. data/lib/zen/public/admin/zen/js/lib/form.js +229 -0
  66. data/lib/zen/public/admin/zen/js/lib/hash.js +0 -27
  67. data/lib/zen/task/spelling.rake +97 -0
  68. data/lib/zen/task/test.rake +21 -0
  69. data/lib/zen/theme.rb +80 -24
  70. data/lib/zen/validation.rb +1 -1
  71. data/lib/zen/version.rb +1 -1
  72. data/proto/app/config/config.rb.erb +9 -4
  73. data/proto/app/config/middlewares.rb +1 -2
  74. data/spec/README.md +56 -0
  75. data/spec/zen/controller/admin_controller.rb +0 -1
  76. data/spec/zen/controller/preview.rb +0 -1
  77. data/spec/zen/package.rb +32 -0
  78. data/spec/zen/package/categories/controller/categories.rb +7 -0
  79. data/spec/zen/package/categories/controller/category_groups.rb +7 -0
  80. data/spec/zen/package/comments/controller/comments.rb +7 -0
  81. data/spec/zen/package/custom_fields/controller/custom_field_groups.rb +7 -0
  82. data/spec/zen/package/custom_fields/controller/custom_field_types.rb +7 -0
  83. data/spec/zen/package/custom_fields/controller/custom_fields.rb +7 -0
  84. data/spec/zen/package/menus/controller/menu_items.rb +7 -0
  85. data/spec/zen/package/menus/controller/menus.rb +7 -0
  86. data/spec/zen/package/menus/helper/menu_frontend.rb +1 -1
  87. data/spec/zen/package/sections/controller/section_entries.rb +39 -0
  88. data/spec/zen/package/sections/controller/sections.rb +14 -0
  89. data/spec/zen/package/users/controller/user_groups.rb +7 -0
  90. data/spec/zen/package/users/controller/users.rb +7 -0
  91. data/zen.gemspec +6 -5
  92. metadata +142 -40
@@ -47,8 +47,27 @@ window.addEvent('domready', function()
47
47
  });
48
48
  });
49
49
 
50
- $$('form[data-autosave-url]').each(function(el)
50
+ $$('form:not(#search_form)').each(function(form)
51
51
  {
52
- new Zen.Autosave(el, el.get('data-autosave-url'));
52
+ new Zen.Form(form);
53
+ });
54
+
55
+ $$('.message').addEvent('click', function()
56
+ {
57
+ var _this = this;
58
+ var fx = new Fx.Tween(
59
+ this,
60
+ {property: 'margin-top', duration: 'short'}
61
+ );
62
+
63
+ fx.start(0, -this.getHeight()).chain(function()
64
+ {
65
+ _this.destroy();
66
+
67
+ if ( $$('.message').length <= 0 )
68
+ {
69
+ $('message_container').destroy();
70
+ }
71
+ });
53
72
  });
54
73
  });
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Adds support for the onhashchange event when using ``window.addEvent()``.
5
+ *
6
+ * Basic usage is as following:
7
+ *
8
+ * window.addEvent('hashchange', function(e, hash)
9
+ * {
10
+ * console.log(hash);
11
+ * });
12
+ *
13
+ * Do note that this implementation does not offer any backwards compatibility
14
+ * with browsers that don't natively support ``window.onhashchange``.
15
+ *
16
+ * @since 21-12-2011
17
+ */
18
+ Element.Events.hashchange = {
19
+ onAdd: function()
20
+ {
21
+ var _this = this;
22
+
23
+ this.onhashchange = function()
24
+ {
25
+ this.fireEvent('hashchange');
26
+ };
27
+ }
28
+ };
29
+
30
+ /**
31
+ * Adds support for the "invalid" event that is raised for invalid form fields.
32
+ *
33
+ * @since 10-03-2012
34
+ */
35
+ Element.Events.invalid = {
36
+ onAdd: function()
37
+ {
38
+ var _this = this;
39
+
40
+ this.oninvalid = function()
41
+ {
42
+ _this.fireEvent('invalid');
43
+ };
44
+ }
45
+ };
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+
3
+ namespace('Zen');
4
+
5
+ /**
6
+ * Zen.Form is a class used for adding various dynamic features to HTML forms
7
+ * such as automatically saving the data and handling validation errors using
8
+ * the HTML5 constraints API.
9
+ *
10
+ * For the features related to the HTML5 constraints API to work properly users
11
+ * should be using a modern browser. If this API is not implemented it will be
12
+ * silently ignored. Automatically saving data is supported regardless of the
13
+ * existence of this API and is handled by Zen.Autosave.
14
+ *
15
+ * ## Usage
16
+ *
17
+ * In order to use this class you'll have to create a new instance of it and
18
+ * pass an instance of Element to it (an instance for a ``<form>`` element):
19
+ *
20
+ * new Zen.Form($('my_form'));
21
+ *
22
+ * By passing and object to the second parameter you can customize various parts
23
+ * of this class. The following options are available:
24
+ *
25
+ * * autosave_attribute: name of the attribute that contains the URL to send
26
+ * requests to when automatically saving form data. This option is set to
27
+ * "data-autosave-url" by default.
28
+ * * tabs_selector: a CSS selector used to determine the tabs for the form, set
29
+ * to "div.tabs ul" by default.
30
+ * * error_class: the primary class to use for error indicators in a tab, set to
31
+ * "tab_error" by default.
32
+ *
33
+ * An example of setting one of these options is the following:
34
+ *
35
+ * new Zen.Form($('my_form'), {error_class: 'custom_tab_error'});
36
+ *
37
+ * Keep in mind that out of the box there's no need to manually use this class,
38
+ * Zen already does this for you.
39
+ *
40
+ * ## Tabs and Errors
41
+ *
42
+ * This class makes it easier for users to nice form errors. This is achieved by
43
+ * showing a small error icon in a tab of which the corresponding tab field
44
+ * contains a number of invalid form elements. For this to work properly you
45
+ * should add the class "tab_field" to each tab field:
46
+ *
47
+ * <div id="general" class="tab_field">
48
+ * <!-- Tab field's content -->
49
+ * </div>
50
+ *
51
+ * @since 12-03-2012
52
+ */
53
+ Zen.Form = new Class(
54
+ {
55
+ Implements: Options,
56
+
57
+ /**
58
+ * The form element for this class.
59
+ *
60
+ * @since 12-03-2012
61
+ */
62
+ element: null,
63
+
64
+ /**
65
+ * Object containing the default and custom defined options merged together.
66
+ *
67
+ * @since 12-03-2012
68
+ */
69
+ options:
70
+ {
71
+ // String containing the name of the attribute that contains the URL to
72
+ // use for automatically saving forms.
73
+ autosave_attribute: 'data-autosave-url',
74
+
75
+ // CSS selector to use for matching the list container of a set of
76
+ // tabs.
77
+ tabs_selector: 'div.tabs ul',
78
+
79
+ // The class to apply to the error indicators.
80
+ error_class: 'tab_error'
81
+ },
82
+
83
+ /**
84
+ * Creates and prepares a new instance of the class.
85
+ *
86
+ * @since 12-03-2012
87
+ * @param {Element} element The form element to use for the instance of this
88
+ * class.
89
+ * @param {Object} options An object containing custom options to use for
90
+ * a particular instance of this class.
91
+ */
92
+ initialize: function(element, options)
93
+ {
94
+ if ( typeOf(element) !== 'element' )
95
+ {
96
+ throw new TypeError(
97
+ 'Expected an element as the first parameter but got '
98
+ + typeOf(element)
99
+ + ' instead'
100
+ );
101
+ }
102
+
103
+ this.setOptions(options);
104
+
105
+ this.element = element;
106
+
107
+ var _this = this;
108
+ var autosave = this.element.get(this.options.autosave_attribute);
109
+
110
+ if ( autosave )
111
+ {
112
+ new Zen.Autosave(this.element, autosave);
113
+ }
114
+
115
+ // Don't bother with the errors and such if the browser vendor is too
116
+ // lazy to properly implement the HTML5 specification.
117
+ if ( !this.element.checkValidity ) return;
118
+
119
+ // Don't bother with the validation bit if the form has no tab fields
120
+ // either.
121
+ if ( this.element.getElements('.tab_field').length <= 0 ) return;
122
+
123
+ this.element.getElements('*').each(function(el)
124
+ {
125
+ var type = el.get('type');
126
+ var event_name = 'blur';
127
+
128
+ // Ignore hidden fields
129
+ if ( type === 'hidden' ) return;
130
+
131
+ // Checkboxes and radio buttons don't call the "blur" event,
132
+ // they instead use "change".
133
+ if ( type === 'checkbox' || type === 'radio' )
134
+ {
135
+ event_name = 'change';
136
+ }
137
+
138
+ el.addEvent('invalid', function()
139
+ {
140
+ _this.invalidElement(this);
141
+ });
142
+
143
+ el.addEvent(event_name, function()
144
+ {
145
+ _this.blurElement(this);
146
+ });
147
+ });
148
+ },
149
+
150
+ /**
151
+ * Handles the "invalid" event of individual form elements. In case an
152
+ * element is invalid a small icon indicating that there is invalid data
153
+ * will be displayed in the tab the field belongs to.
154
+ *
155
+ * @since 12-03-2012
156
+ * @param {Element} element The invalid form element.
157
+ */
158
+ invalidElement: function(element)
159
+ {
160
+ var tab = this.getTab(element);
161
+
162
+ if ( !tab ) return;
163
+
164
+ var li = tab.getParent('li');
165
+
166
+ if ( li.getElements('.' + this.options.error_class).length <= 0 )
167
+ {
168
+ var error = new Element('span',
169
+ {
170
+ 'class': this.options.error_class + ' icon error'
171
+ });
172
+
173
+ error.inject(tab, 'before');
174
+ }
175
+ },
176
+
177
+ /**
178
+ * Handles the "blur" event of an individual form element. This event is
179
+ * used to check if all the errors in a tab field are gone and if so will
180
+ * remove the error indicator.
181
+ *
182
+ * @since 12-03-2012
183
+ * @param {Element} element The form element that lost focus.
184
+ */
185
+ blurElement: function(element)
186
+ {
187
+ var field = element.getParent('.tab_field');
188
+
189
+ if ( !field ) return;
190
+
191
+ if ( field.getElements(':invalid').length <= 0 )
192
+ {
193
+ var tab = this.getTab(element);
194
+
195
+ if ( !tab ) return;
196
+
197
+ // Get the error indicator and remove it if needed.
198
+ var error = tab.getParent('li')
199
+ .getElement('.' + this.options.error_class);
200
+
201
+ if ( error )
202
+ {
203
+ error.destroy();
204
+ }
205
+ }
206
+ },
207
+
208
+ /**
209
+ * Given a form element (or any other kind of element in a form) this method
210
+ * will try to retrieve the tab field the element belongs to.
211
+ *
212
+ * @since 12-03-2012
213
+ * @param {Element} element The element for which to retrieve the tab
214
+ * field.
215
+ * @return {Element|null}
216
+ */
217
+ getTab: function(element)
218
+ {
219
+ var field = element.getParent('.tab_field');
220
+ var tabs = $$(this.options.tabs_selector);
221
+
222
+ if ( tabs.length <= 0 || !field )
223
+ {
224
+ return null;
225
+ }
226
+
227
+ return tabs[0].getElement('a[href="#' + field.get('id') + '"]');
228
+ },
229
+ });
@@ -2,33 +2,6 @@
2
2
 
3
3
  namespace('Zen');
4
4
 
5
- /**
6
- * Adds support for the onhashchange event when using ``window.addEvent()``.
7
- *
8
- * Basic usage is as following:
9
- *
10
- * window.addEvent('hashchange', function(e, hash)
11
- * {
12
- * console.log(hash);
13
- * });
14
- *
15
- * Do note that this implementation does not offer any backwards compatibility
16
- * with browsers that don't natively support ``window.onhashchange``.
17
- *
18
- * @since 21-12-2011
19
- */
20
- Element.Events.hashchange = {
21
- onAdd: function()
22
- {
23
- var _this = this;
24
-
25
- this.onhashchange = function()
26
- {
27
- this.fireEvent('hashchange');
28
- };
29
- }
30
- };
31
-
32
5
  /**
33
6
  * Zen.Hash is a class that can be used to parse and generate shebang/hash bang
34
7
  * URLs. Parsing is done using ``Zen.Hash#parse`` and generating URLs using
@@ -0,0 +1,97 @@
1
+ # Task that checks the documentation for spelling errors using Raspell/Aspell.
2
+ # This task requires Aspell to be installed along with an English dictionary. On
3
+ # Arch Linux these can be installed as following:
4
+ #
5
+ # $ sudo pacman -S aspell aspell-en
6
+ #
7
+ desc 'Search docs for spelling errors'
8
+ task :spelling do
9
+ require 'raspell'
10
+ require 'ripper'
11
+
12
+ speller = Aspell.new1(
13
+ 'lang' => 'en',
14
+ 'personal' => File.expand_path('../../../../.aspell.en.pws', __FILE__),
15
+ 'ignore-case' => 'true'
16
+ )
17
+
18
+ base_dir = File.expand_path('../../../..', __FILE__)
19
+ files = Dir['lib/zen/**/*.rb']
20
+ exclude_lines = [/^#\s*@/, /^#\s{2,}/, /^#\s*!\[/, /^#\s*\[/]
21
+ exclude_patterns = [/\d+/, /_+/, /^`/]
22
+
23
+ files.each do |file|
24
+ file = File.expand_path(file)
25
+ relative = file.gsub(/^#{base_dir}\//, '')
26
+ errors = []
27
+ content = File.read(file, File.size(file))
28
+
29
+ Ripper.lex(content).each do |group|
30
+ next unless group[1] == :on_comment
31
+
32
+ skip_line = false
33
+
34
+ # Determine whether or not the entire line should be skipped.
35
+ exclude_lines.each do |pattern|
36
+ if group[2] =~ pattern
37
+ skip_line = true
38
+ break
39
+ end
40
+ end
41
+
42
+ next if skip_line
43
+
44
+ # Extract each word out of the line.
45
+ group[2].gsub(/[\w'-]+/).each do |word|
46
+ skip = false
47
+ word = word.gsub(/^'|'$/, '')
48
+
49
+ # Determine if the word should be ignored based on a list of blacklisted
50
+ # patterns.
51
+ exclude_patterns.each do |pattern|
52
+ if word =~ pattern
53
+ skip = true
54
+ break
55
+ end
56
+ end
57
+
58
+ # Exclude words that are actually constants (e.g. FalseClass). This
59
+ # isn't very fast but it prevents the requirement of using a few nasty
60
+ # regular expressions.
61
+ unless skip
62
+ lexed = Ripper.lex(word)
63
+
64
+ if lexed[0] and lexed[0][1] == :on_const
65
+ skip = true
66
+ end
67
+ end
68
+
69
+ next if skip == true
70
+
71
+ unless speller.check(word)
72
+ suggested = speller.suggest(word)[0]
73
+
74
+ if suggested
75
+ errors << {
76
+ :line => group[0][0],
77
+ :column => group[0][1],
78
+ :word => word,
79
+ :suggestion => suggested
80
+ }
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ unless errors.empty?
87
+ puts
88
+ puts relative
89
+ puts
90
+
91
+ errors.each do |error|
92
+ puts " * line ##{error[:line]}, column ##{error[:column]}: " \
93
+ "#{error[:word]}, suggestion: #{error[:suggestion]}"
94
+ end
95
+ end
96
+ end
97
+ end
@@ -31,4 +31,25 @@ namespace :test do
31
31
 
32
32
  sh(command)
33
33
  end
34
+
35
+ # Task that ensures that the various Travis CI tests each use their own
36
+ # database based on the Ruby version.
37
+ desc 'Runs the tests for Travis CI'
38
+ task :travis do
39
+ suffix = '_' + RUBY_VERSION.gsub('.', '_')
40
+
41
+ if ENV['DATABASE']
42
+ if ENV['ADAPTER'] and ENV['ADAPTER'] == 'sqlite'
43
+ split = ENV['DATABASE'].split('.')
44
+
45
+ ENV['DATABASE'] = split[0] + suffix + '.' + split[1]
46
+ else
47
+ ENV['DATABASE'] += suffix
48
+ end
49
+ end
50
+
51
+ Dir.chdir(spec_dir)
52
+
53
+ sh(command)
54
+ end
34
55
  end