Admin Menu Editor - Version 1.6.2

Version Description

  • Fixed a bug that made menu items "jump" slightly to the left when you start to drag them.
  • Fixed a Multisite-specific bug where temporarily switching to another site using the switch_to_blog() function could result in the user having the wrong permissions.
  • When saving settings, the plugin will now compress the menu data before sending it to the server. This reduces the chances of exceeding request size limits that are imposed by some hosting companies.
  • You can dismiss the "Settings saved" notification by clicking the "x" button.
  • Tested up to WordPress 4.5.2.
Download this release

Release Info

Developer whiteshadow
Plugin Icon 128x128 Admin Menu Editor
Version 1.6.2
Comparing to
See all releases

Code changes from version 1.6.1 to 1.6.2

css/menu-editor.css CHANGED
@@ -144,7 +144,7 @@
144
display: block;
145
width: 290px;
146
padding: 3px;
147
- margin: 2px auto; }
148
149
.ws_submenu {
150
min-height: 2em; }
144
display: block;
145
width: 290px;
146
padding: 3px;
147
+ margin: 2px 0 2px 6px; }
148
149
.ws_submenu {
150
min-height: 2em; }
css/menu-editor.scss CHANGED
@@ -12,13 +12,16 @@
12
margin-top: 9px;
13
}
14
15
.ws_main_container {
16
margin: 2px;
17
- width: 310px;
18
float: left;
19
display:block;
20
21
- border: 1px solid #cdd5d5;
22
background-color: #FFFFFF;
23
24
border-radius: 3px;
@@ -204,11 +207,16 @@
204
*/
205
206
.ws_container {
207
display: block;
208
- width: 290px;
209
210
- padding : 3px;
211
- margin: 2px auto;
212
}
213
214
.ws_active { }
12
margin-top: 9px;
13
}
14
15
+ $mainContainerWidth: 310px;
16
+ $mainContainerBorderWidth: 1px;
17
+
18
.ws_main_container {
19
margin: 2px;
20
+ width: $mainContainerWidth;
21
float: left;
22
display:block;
23
24
+ border: $mainContainerBorderWidth solid #cdd5d5;
25
background-color: #FFFFFF;
26
27
border-radius: 3px;
207
*/
208
209
.ws_container {
210
+ $itemWidth: 290px;
211
+ $itemPadding: 3px;
212
+ $itemBorderWidth: 1px;
213
+ $itemHorizontalMargin: ($mainContainerWidth - $itemWidth - $itemPadding * 2 - $itemBorderWidth * 2) / 2;
214
+
215
display: block;
216
+ width: $itemWidth;
217
218
+ padding : $itemPadding;
219
+ margin: 2px 0 2px $itemHorizontalMargin;
220
}
221
222
.ws_active { }
css/style-modern-one.css CHANGED
@@ -13,6 +13,7 @@ $hiddenItemBorder: #f1acb1;
13
padding: 0;
14
margin-top: 0;
15
margin-bottom: 9px;
16
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); }
17
.ws_container .ws_item_title {
18
color: #23282D;
@@ -116,7 +117,7 @@ $hiddenItemBorder: #f1acb1;
116
#ws_menu_editor #ws_toggle_editor_layout {
117
display: block; }
118
119
- #ws_icon_selector {
120
z-index: 3; }
121
122
.ws_container.ui-sortable-helper {
13
padding: 0;
14
margin-top: 0;
15
margin-bottom: 9px;
16
+ margin-left: 10px;
17
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); }
18
.ws_container .ws_item_title {
19
color: #23282D;
117
#ws_menu_editor #ws_toggle_editor_layout {
118
display: block; }
119
120
+ #ws_icon_selector, #ws_embedded_page_selector {
121
z-index: 3; }
122
123
.ws_container.ui-sortable-helper {
css/style-modern-one.scss CHANGED
@@ -27,7 +27,12 @@ $hiddenItemBackground: darken($itemBackground, 9);
27
$hiddenItemBorder: $itemBorder;
28
$hiddenItemText: #888; //#9a9ea5; //#82878C; //#999
29
30
.ws_container {
31
border: 0 solid transparent;
32
background: $itemBackground;
33
@@ -40,6 +45,7 @@ $hiddenItemText: #888; //#9a9ea5; //#82878C; //#999
40
41
margin-top: 0;
42
margin-bottom: $itemMarginBottom;
43
44
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
45
@@ -231,12 +237,11 @@ $hiddenItemText: #888; //#9a9ea5; //#82878C; //#999
231
display: block;
232
}
233
234
- //==============================================
235
- // Icon selector
236
- //==============================================
237
238
- #ws_icon_selector {
239
- //Appear above the selected item.
240
z-index: 3;
241
}
242
@@ -252,10 +257,8 @@ $hiddenItemText: #888; //#9a9ea5; //#82878C; //#999
252
// Columns / containers
253
//==============================================
254
255
- $columnPadding: 10px;
256
-
257
.ws_main_container {
258
- width: $itemWidth + $columnPadding * 2;
259
260
.ws_toolbar {
261
padding: $columnPadding $columnPadding 0;
27
$hiddenItemBorder: $itemBorder;
28
$hiddenItemText: #888; //#9a9ea5; //#82878C; //#999
29
30
+ $columnPadding: 10px;
31
+ $mainContainerWidth: $itemWidth + $columnPadding * 2;
32
+
33
.ws_container {
34
+ $itemHorizontalMargin: ($mainContainerWidth - $itemWidth) / 2;
35
+
36
border: 0 solid transparent;
37
background: $itemBackground;
38
45
46
margin-top: 0;
47
margin-bottom: $itemMarginBottom;
48
+ margin-left: $itemHorizontalMargin;
49
50
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
51
237
display: block;
238
}
239
240
+ //====================================================
241
+ // Dropdowns should appear above the selected item.
242
+ //====================================================
243
244
+ #ws_icon_selector, #ws_embedded_page_selector {
245
z-index: 3;
246
}
247
257
// Columns / containers
258
//==============================================
259
260
.ws_main_container {
261
+ width: $mainContainerWidth;
262
263
.ws_toolbar {
264
padding: $columnPadding $columnPadding 0;
includes/ajax-helper.php ADDED
@@ -0,0 +1,178 @@
1
+ <?php
2
+ class ameAjaxAction {
3
+ protected $action = '';
4
+ protected $handler;
5
+
6
+ protected $requiredParameters = array();
7
+ protected $defaultParameters = array();
8
+ protected $checkAuthorization;
9
+
10
+ public function __construct($uniqueActionName = null, $handler = null) {
11
+ if (isset($uniqueActionName)) {
12
+ $this->action = $uniqueActionName;
13
+ }
14
+ if (isset($handler)) {
15
+ $this->handler = $handler;
16
+ }
17
+
18
+ if (empty($this->action)) {
19
+ throw new LogicException(sprintf(
20
+ 'AJAX action name is missing. You must either pass it to the %1$s constructor '
21
+ . 'or give the %1$s::$action property a valid default value.',
22
+ get_class($this)
23
+ ));
24
+ }
25
+
26
+ $hookName = 'wp_ajax_' . $this->action;
27
+ if (has_action($hookName)) {
28
+ throw new RuntimeException(sprintf('The action name "%s" is already in use.', $this->action));
29
+ }
30
+ add_action('wp_ajax_' . $this->action, array($this, '_processRequest'));
31
+ }
32
+
33
+ /**
34
+ * @access protected
35
+ */
36
+ public function _processRequest() {
37
+ //Check nonce.
38
+ if (!check_ajax_referer($this->action, false, false)) {
39
+ $this->exitWithError(
40
+ 'Access denied. Invalid nonce',
41
+ 'invalid_nonce'
42
+ );
43
+ }
44
+
45
+ $method = filter_input(INPUT_SERVER, 'REQUEST_METHOD');
46
+
47
+ //Retrieve request parameters.
48
+ $params = array();
49
+ if ($method === 'GET') {
50
+ $params = $_GET;
51
+ } else if ($method === 'POST') {
52
+ $params = $_POST;
53
+ }
54
+
55
+ //Remove magic quotes. WordPress applies them in wp-settings.php.
56
+ if (did_action('sanitize_comment_cookies') && function_exists('wp_magic_quotes')) {
57
+ $params = wp_unslash($params);
58
+ }
59
+
60
+ //Verify that all of the required parameters are present. Empty strings are not allowed.
61
+ foreach($this->requiredParameters as $name) {
62
+ if (!isset($params[$name]) || ($params[$name] === '')) {
63
+ $this->exitWithError(
64
+ sprintf('The required parameter "%s" is missing or empty.', $name),
65
+ 'missing_required_parameter'
66
+ );
67
+ }
68
+ }
69
+
70
+ //Apply defaults.
71
+ $params = array_merge($this->defaultParameters, $params);
72
+
73
+ //Run custom authorization checks.
74
+ $isAllowed = $this->isUserAuthorized($params);
75
+ if ($isAllowed instanceof WP_Error) {
76
+ $this->exitWithError($isAllowed->get_error_message(), $isAllowed->get_error_code());
77
+ } else if (!$isAllowed) {
78
+ $this->exitWithError(
79
+ sprintf('You don\'t have permission to perform the "%s" action.', $this->action),
80
+ 'access_denied'
81
+ );
82
+ }
83
+
84
+ //Finally, perform the action.
85
+ $response = $this->handleAction($params);
86
+ if ($response instanceof WP_Error) {
87
+ $this->exitWithError($response->get_error_message(), $response->get_error_code());
88
+ }
89
+ $this->outputResponse($response);
90
+ exit;
91
+ }
92
+
93
+ protected function handleAction($params) {
94
+ if (is_callable($this->handler)) {
95
+ return call_user_func($this->handler, $params);
96
+ } else {
97
+ $this->exitWithError(
98
+ sprintf(
99
+ 'There is no request handler assigned to the "%1$s" action. '
100
+ . 'Either override the %3$s method or set %2$s::$handler to a valid callback.',
101
+ $this->action,
102
+ __CLASS__,
103
+ __METHOD__
104
+ ),
105
+ 'missing_ajax_handler'
106
+ );
107
+ return null;
108
+ }
109
+ }
110
+
111
+ protected function exitWithError($message, $code = null) {
112
+ if ( ($message === '') && !empty($code) ) {
113
+ $message = $code;
114
+ }
115
+
116
+ $response = array(
117
+ 'error' => array(
118
+ 'message' => $message,
119
+ 'code' => $code,
120
+ )
121
+ );
122
+ $this->outputResponse($response);
123
+ exit;
124
+ }
125
+
126
+ protected function outputResponse($response) {
127
+ header('Content-Type: application/json');
128
+ echo json_encode($response);
129
+ }
130
+
131
+ /**
132
+ * Check if the current user is authorized to perform this action.
133
+ *
134
+ * @param array $params Request parameters.
135
+ * @return bool|WP_Error
136
+ */
137
+ protected function isUserAuthorized($params) {
138
+ if (isset($this->checkAuthorization)) {
139
+ return call_user_func($this->checkAuthorization, $params);
140
+ }
141
+ return true;
142
+ }
143
+
144
+ //Just a bunch of fluent setters.
145
+ //-------------------------------
146
+
147
+ /**
148
+ * @param string ...$param One or more parameter names.
149
+ * @return $this
150
+ */
151
+ public function setRequiredParams($param) {
152
+ $params = func_get_args();
153
+ if (count($params) === 1 && is_array($params[0])) {
154
+ $params = $params[0];
155
+ }
156
+
157
+ $this->requiredParameters = $params;
158
+ return $this;
159
+ }
160
+
161
+ /**
162
+ * @param callable $handler
163
+ * @return $this
164
+ */
165
+ public function setHandler($handler) {
166
+ $this->handler = $handler;
167
+ return $this;
168
+ }
169
+
170
+ /**
171
+ * @param callable $callback
172
+ * @return $this
173
+ */
174
+ public function setAuthCallback($callback) {
175
+ $this->checkAuthorization = $callback;
176
+ return $this;
177
+ }
178
+ }
includes/editor-page.php CHANGED
@@ -64,7 +64,7 @@ if ( !apply_filters('admin_menu_editor_is_pro', false) ){
64
<?php
65
if ( !empty($_GET['message']) ){
66
if ( intval($_GET['message']) == 1 ){
67
- echo '<div id="message" class="updated fade"><p><strong>Settings saved.</strong></p></div>';
68
} elseif ( intval($_GET['message']) == 2 ) {
69
echo '<div id="message" class="error"><p><strong>Failed to decode input! The menu wasn\'t modified.</strong></p></div>';
70
}
64
<?php
65
if ( !empty($_GET['message']) ){
66
if ( intval($_GET['message']) == 1 ){
67
+ echo '<div id="message" class="updated notice is-dismissible"><p><strong>Settings saved.</strong></p></div>';
68
} elseif ( intval($_GET['message']) == 2 ) {
69
echo '<div id="message" class="error"><p><strong>Failed to decode input! The menu wasn\'t modified.</strong></p></div>';
70
}
includes/menu-editor-core.php CHANGED
@@ -14,6 +14,7 @@ require $thisDirectory . '/role-utils.php';
14
require $thisDirectory . '/menu-item.php';
15
require $thisDirectory . '/menu.php';
16
require $thisDirectory . '/auto-versioning.php';
17
18
class WPMenuEditor extends MenuEd_ShadowPluginFramework {
19
const WPML_CONTEXT = 'admin-menu-editor menu texts';
@@ -186,6 +187,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
186
'index.php?page=dwqa-about' => true,
187
'index.php?page=dwqa-changelog' => true,
188
'index.php?page=dwqa-credits' => true,
189
);
190
191
//AJAXify screen options
@@ -231,6 +237,9 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
231
//There's also a "set_user_role" hook, but it's only called by WP_User::set_role and not WP_User::add_role.
232
//It's also redundant - WP_User::set_role updates user meta, so the above hooks already cover it.
233
234
add_action('admin_menu_editor-display_tabs', array($this, 'display_editor_tabs'));
235
236
//Modules
@@ -239,7 +248,11 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
239
240
$proModuleDirectory = AME_ROOT_DIR . '/extras/modules';
241
if ( @is_dir($proModuleDirectory) ) {
242
- if ( is_file($proModuleDirectory . '/dashboard-widget-editor/load.php') ) {
243
require_once $proModuleDirectory . '/dashboard-widget-editor/load.php';
244
new ameWidgetEditor($this);
245
}
@@ -340,8 +353,15 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
340
if ( $this->current_user_can_edit_menu() ){
341
$this->log_security_note('Current user can edit the admin menu.');
342
343
$page = add_options_page(
344
- apply_filters('admin_menu_editor-self_page_title', 'Menu Editor'),
345
apply_filters('admin_menu_editor-self_menu_title', 'Menu Editor'),
346
apply_filters('admin_menu_editor-capability', 'manage_options'),
347
'menu_editor',
@@ -365,9 +385,6 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
365
366
//Make a placeholder for our screen options (hacky)
367
add_meta_box("ws-ame-screen-options", "[AME placeholder]", '__return_false', $page);
368
-
369
- //Determine the current menu editor page tab.
370
- $this->current_tab = isset($this->get['sub_section']) ? strval($this->get['sub_section']) : 'editor';
371
}
372
373
//Store the "original" menus for later use in the editor
@@ -594,7 +611,7 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
594
*
595
* @return void
596
*/
597
- function enqueue_scripts(){
598
//Optimization: Remove wp-emoji.js from the plugin page. wpEmoji makes DOM manipulation slow because
599
//it tracks *all* DOM changes using MutationObserver.
600
remove_action('admin_print_scripts', 'print_emoji_detection_script');
@@ -2128,6 +2145,22 @@ class WPMenuEditor extends MenuEd_ShadowPluginFramework {
2128
return $capability;
2129
}
2130
2131
/**
2132
* Create a virtual 'super_admin' capability that only super admins have.
2133
* This function accomplishes that by by filtering 'user_has_cap' calls.
14
require $thisDirectory . '/menu-item.php';
15
require $thisDirectory . '/menu.php';
16
require $thisDirectory . '/auto-versioning.php';
17
+ require $thisDirectory . '/ajax-helper.php';
18
19
class WPMenuEditor extends MenuEd_ShadowPluginFramework {
20
const WPML_CONTEXT = 'admin-menu-editor menu texts';
187
'index.php?page=dwqa-about' => true,
188
'index.php?page=dwqa-changelog' => true,
189
'index.php?page=dwqa-credits' => true,
190
+ //Ninja Forms 2.9.41
191
+ 'index.php?page=nf-about' => true,
192
+ 'index.php?page=nf-changelog' => true,
193
+ 'index.php?page=nf-getting-started' => true,
194
+ 'index.php?page=nf-credits' => true,
195
);
196
197
//AJAXify screen options
237
//There's also a "set_user_role" hook, but it's only called by WP_User::set_role and not WP_User::add_role.
238
//It's also redundant - WP_User::set_role updates user meta, so the above hooks already cover it.
239
240
+ //Multisite: Clear role and capability caches when switching to another site.
241
+ add_action('switch_blog', array($this, 'clear_site_specific_caches'), 10, 0);
242
+
243
add_action('admin_menu_editor-display_tabs', array($this, 'display_editor_tabs'));
244
245
//Modules
248
249
$proModuleDirectory = AME_ROOT_DIR . '/extras/modules';
250
if ( @is_dir($proModuleDirectory) ) {
251
+ //The widget module requires PHP 5.3.
252
+ if (
253
+ version_compare(phpversion(), '5.3', '>=')
254
+ && is_file($proModuleDirectory . '/dashboard-widget-editor/load.php')
255
+ ) {
256
require_once $proModuleDirectory . '/dashboard-widget-editor/load.php';
257
new ameWidgetEditor($this);
258
}
353
if ( $this->current_user_can_edit_menu() ){
354
$this->log_security_note('Current user can edit the admin menu.');
355
356
+ //Determine the current menu editor page tab.
357
+ $this->current_tab = isset($this->get['sub_section']) ? strval($this->get['sub_section']) : 'editor';
358
+ $tab_title = '';
359
+ if ($this->current_tab !== 'editor' && isset($this->tabs[$this->current_tab])) {
360
+ $tab_title = ' - ' . $this->tabs[$this->current_tab];
361
+ }
362
+
363
$page = add_options_page(
364
+ apply_filters('admin_menu_editor-self_page_title', 'Menu Editor') . $tab_title,
365
apply_filters('admin_menu_editor-self_menu_title', 'Menu Editor'),
366
apply_filters('admin_menu_editor-capability', 'manage_options'),
367
'menu_editor',
385
386
//Make a placeholder for our screen options (hacky)
387
add_meta_box("ws-ame-screen-options", "[AME placeholder]", '__return_false', $page);
388
}
389
390
//Store the "original" menus for later use in the editor
611
*
612
* @return void
613
*/
614
+ function enqueue_scripts() {
615
//Optimization: Remove wp-emoji.js from the plugin page. wpEmoji makes DOM manipulation slow because
616
//it tracks *all* DOM changes using MutationObserver.
617
remove_action('admin_print_scripts', 'print_emoji_detection_script');
2145
return $capability;
2146
}
2147
2148
+ /**
2149
+ * Clear all internal caches that can vary depending on the current site.
2150
+ *
2151
+ * For example, the same user can have different roles on different sites,
2152
+ * so we must clear the role cache when WordPress switches the active site.
2153
+ */
2154
+ public function clear_site_specific_caches() {
2155
+ $this->cached_virtual_caps = null;
2156
+ $this->cached_user_caps = array();
2157
+ $this->cached_user_roles = array();
2158
+
2159
+ if ($this->options['menu_config_scope'] === 'site') {
2160
+ $this->cached_custom_menu = null;
2161
+ }
2162
+ }
2163
+
2164
/**
2165
* Create a virtual 'super_admin' capability that only super admins have.
2166
* This function accomplishes that by by filtering 'user_has_cap' calls.
includes/menu.php CHANGED
@@ -478,4 +478,6 @@ class ameGrantedCapabilityFilter {
478
}
479
480
481
- class InvalidMenuException extends Exception {}
478
}
479
480
481
+ class InvalidMenuException extends Exception {}
482
+
483
+ class ameInvalidJsonException extends RuntimeException {};
js/actor-manager.js CHANGED
@@ -55,6 +55,15 @@ var AmeRole = (function (_super) {
55
this.actorTypeSpecificity = 1;
56
this.name = roleId;
57
}
58
return AmeRole;
59
}(AmeBaseActor));
60
var AmeUser = (function (_super) {
@@ -264,14 +273,14 @@ var AmeActorManager = (function () {
264
AmeActorManager.setCapInContext = function (context, actor, capability, hasCap, sourceType, sourceName) {
265
capability = AmeActorManager.mapMetaCap(capability);
266
var grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap;
267
- _.set(context, [actor, capability], grant);
268
};
269
AmeActorManager.prototype.resetCap = function (actor, capability) {
270
AmeActorManager.resetCapInContext(this.grantedCapabilities, actor, capability);
271
};
272
AmeActorManager.resetCapInContext = function (context, actor, capability) {
273
capability = AmeActorManager.mapMetaCap(capability);
274
- if (_.has(context, [actor, capability])) {
275
delete context[actor][capability];
276
}
277
};
55
this.actorTypeSpecificity = 1;
56
this.name = roleId;
57
}
58
+ AmeRole.prototype.hasOwnCap = function (capability) {
59
+ //In WordPress, a role name is also a capability name. Users that have the role "foo" always
60
+ //have the "foo" capability. It's debatable whether the role itself actually has that capability
61
+ //(WP_Role says no), but it's convenient to treat it that way.
62
+ if (capability === this.name) {
63
+ return true;
64
+ }
65
+ return _super.prototype.hasOwnCap.call(this, capability);
66
+ };
67
return AmeRole;
68
}(AmeBaseActor));
69
var AmeUser = (function (_super) {
273
AmeActorManager.setCapInContext = function (context, actor, capability, hasCap, sourceType, sourceName) {
274
capability = AmeActorManager.mapMetaCap(capability);
275
var grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap;
276
+ AmeActorManager._.set(context, [actor, capability], grant);
277
};
278
AmeActorManager.prototype.resetCap = function (actor, capability) {
279
AmeActorManager.resetCapInContext(this.grantedCapabilities, actor, capability);
280
};
281
AmeActorManager.resetCapInContext = function (context, actor, capability) {
282
capability = AmeActorManager.mapMetaCap(capability);
283
+ if (AmeActorManager._.has(context, [actor, capability])) {
284
delete context[actor][capability];
285
}
286
};
js/actor-manager.ts CHANGED
@@ -68,6 +68,17 @@ class AmeRole extends AmeBaseActor {
68
super('role:' + roleId, displayName, capabilities);
69
this.name = roleId;
70
}
71
}
72
73
class AmeUser extends AmeBaseActor {
@@ -345,7 +356,7 @@ class AmeActorManager {
345
capability = AmeActorManager.mapMetaCap(capability);
346
347
var grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap;
348
- _.set(context, [actor, capability], grant);
349
}
350
351
resetCap(actor: string, capability: string) {
@@ -355,7 +366,7 @@ class AmeActorManager {
355
static resetCapInContext(context: AmeGrantedCapabilityMap, actor: string, capability: string) {
356
capability = AmeActorManager.mapMetaCap(capability);
357
358
- if (_.has(context, [actor, capability])) {
359
delete context[actor][capability];
360
}
361
}
68
super('role:' + roleId, displayName, capabilities);
69
this.name = roleId;
70
}
71
+
72
+
73
+ hasOwnCap(capability: string): boolean {
74
+ //In WordPress, a role name is also a capability name. Users that have the role "foo" always
75
+ //have the "foo" capability. It's debatable whether the role itself actually has that capability
76
+ //(WP_Role says no), but it's convenient to treat it that way.
77
+ if (capability === this.name) {
78
+ return true;
79
+ }
80
+ return super.hasOwnCap(capability);
81
+ }
82
}
83
84
class AmeUser extends AmeBaseActor {
356
capability = AmeActorManager.mapMetaCap(capability);
357
358
var grant = sourceType ? [hasCap, sourceType, sourceName || null] : hasCap;
359
+ AmeActorManager._.set(context, [actor, capability], grant);
360
}
361
362
resetCap(actor: string, capability: string) {
366
static resetCapInContext(context: AmeGrantedCapabilityMap, actor: string, capability: string) {
367
capability = AmeActorManager.mapMetaCap(capability);
368
369
+ if (AmeActorManager._.has(context, [actor, capability])) {
370
delete context[actor][capability];
371
}
372
}
js/jquery.form.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /// <reference path="jquery.d.ts" />
2
+
3
+ interface JQuery {
4
+ //These method are added by the jquery-form plugin.
5
+ ajaxForm: (options: any) => JQuery;
6
+ resetForm: () => JQuery;
7
+ }
js/jquery.form.js CHANGED
@@ -1,16 +1,28 @@
1
/*!
2
* jQuery Form Plugin
3
- * version: 3.24 (26-DEC-2012)
4
- * @requires jQuery v1.5 or later
5
- *
6
* Examples and documentation at: http://malsup.com/jquery/form/
7
* Project repository: https://github.com/malsup/form
8
- * Dual licensed under the MIT and GPL licenses:
9
- * http://malsup.github.com/mit-license.txt
10
- * http://malsup.github.com/gpl-license-v2.txt
11
*/
12
- /*global ActiveXObject alert */
13
- ;(function($) {
14
"use strict";
15
16
/*
@@ -37,7 +49,7 @@
37
target: '#output'
38
});
39
});
40
-
41
You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
42
form does not have to exist when you invoke ajaxForm:
43
@@ -45,7 +57,7 @@
45
delegation: true,
46
target: '#output'
47
});
48
-
49
When using ajaxForm, the ajaxSubmit function will be invoked for you
50
at the appropriate time.
51
*/
@@ -57,6 +69,23 @@ var feature = {};
57
feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
58
feature.formdata = window.FormData !== undefined;
59
60
/**
61
* ajaxSubmit() provides a mechanism for immediately submitting
62
* an HTML form using AJAX.
@@ -69,15 +98,19 @@ $.fn.ajaxSubmit = function(options) {
69
log('ajaxSubmit: skipping submit process - no element selected');
70
return this;
71
}
72
-
73
var method, action, url, $form = this;
74
75
if (typeof options == 'function') {
76
options = { success: options };
77
}
78
79
- method = this.attr('method');
80
- action = this.attr('action');
81
url = (typeof action === 'string') ? $.trim(action) : '';
82
url = url || window.location.href || '';
83
if (url) {
@@ -88,7 +121,7 @@ $.fn.ajaxSubmit = function(options) {
88
options = $.extend(true, {
89
url: url,
90
success: $.ajaxSettings.success,
91
- type: method || 'GET',
92
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
93
}, options);
94
@@ -111,7 +144,7 @@ $.fn.ajaxSubmit = function(options) {
111
if ( traditional === undefined ) {
112
traditional = $.ajaxSettings.traditional;
113
}
114
-
115
var elements = [];
116
var qx, a = this.formToArray(options.semantic, elements);
117
if (options.data) {
@@ -135,7 +168,7 @@ $.fn.ajaxSubmit = function(options) {
135
var q = $.param(a, traditional);
136
if (qx) {
137
q = ( q ? (q + '&' + qx) : qx );
138
- }
139
if (options.type.toUpperCase() == 'GET') {
140
options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
141
options.data = null; // data is null for 'get'
@@ -165,17 +198,33 @@ $.fn.ajaxSubmit = function(options) {
165
}
166
167
options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
168
- var context = options.context || this ; // jQuery 1.4+ supports scope context
169
for (var i=0, max=callbacks.length; i < max; i++) {
170
callbacks[i].apply(context, [data, status, xhr || $form, $form]);
171
}
172
};
173
174
// are there files to upload?
175
176
// [value] (issue #113), also see comment:
177
// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
178
- var fileInputs = $('input[type=file]:enabled[value!=""]', this);
179
180
var hasFileInputs = fileInputs.length > 0;
181
var mp = 'multipart/form-data';
@@ -211,8 +260,9 @@ $.fn.ajaxSubmit = function(options) {
211
$form.removeData('jqxhr').data('jqxhr', jqxhr);
212
213
// clear element array
214
- for (var k=0; k < elements.length; k++)
215
elements[k] = null;
216
217
// fire 'notify' event
218
this.trigger('form-submit-notify', [this, options]);
@@ -220,15 +270,16 @@ $.fn.ajaxSubmit = function(options) {
220
221
// utility fn for deep serialization
222
function deepSerialize(extraData){
223
- var serialized = $.param(extraData).split('&');
224
var len = serialized.length;
225
- var result = {};
226
var i, part;
227
for (i=0; i < len; i++) {
228
// #252; undo param space replacement
229
serialized[i] = serialized[i].replace(/\+/g,' ');
230
part = serialized[i].split('=');
231
- result[decodeURIComponent(part[0])] = decodeURIComponent(part[1]);
232
}
233
return result;
234
}
@@ -243,9 +294,11 @@ $.fn.ajaxSubmit = function(options) {
243
244
if (options.extraData) {
245
var serializedData = deepSerialize(options.extraData);
246
- for (var p in serializedData)
247
- if (serializedData.hasOwnProperty(p))
248
- formdata.append(p, serializedData[p]);
249
}
250
251
options.data = null;
@@ -256,13 +309,13 @@ $.fn.ajaxSubmit = function(options) {
256
cache: false,
257
type: method || 'POST'
258
});
259
-
260
if (options.uploadProgress) {
261
// workaround because jqXHR does not expose upload property
262
s.xhr = function() {
263
- var xhr = jQuery.ajaxSettings.xhr();
264
if (xhr.upload) {
265
- xhr.upload.onprogress = function(event) {
266
var percent = 0;
267
var position = event.loaded || event.position; /*event.position is deprecated*/
268
var total = event.total;
@@ -270,18 +323,25 @@ $.fn.ajaxSubmit = function(options) {
270
percent = Math.ceil(position / total * 100);
271
}
272
options.uploadProgress(event, position, total, percent);
273
- };
274
}
275
return xhr;
276
};
277
}
278
279
s.data = null;
280
- var beforeSend = s.beforeSend;
281
- s.beforeSend = function(xhr, o) {
282
o.data = formdata;
283
- if(beforeSend)
284
- beforeSend.call(this, xhr, o);
285
};
286
return $.ajax(s);
287
}
@@ -289,25 +349,23 @@ $.fn.ajaxSubmit = function(options) {
289
// private function for handling file uploads (hat tip to YAHOO!)
290
function fileUploadIframe(a) {
291
var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
292
- var useProp = !!$.fn.prop;
293
var deferred = $.Deferred();
294
295
- if ($('[name=submit],[id=submit]', form).length) {
296
- // if there is an input with a name or id of 'submit' then we won't be
297
- // able to invoke the submit fn on the form (at least not x-browser)
298
- alert('Error: Form elements must not have name or id of "submit".');
299
- deferred.reject();
300
- return deferred;
301
- }
302
-
303
if (a) {
304
// ensure that every serialized input is still enabled
305
for (i=0; i < elements.length; i++) {
306
el = $(elements[i]);
307
- if ( useProp )
308
el.prop('disabled', false);
309
- else
310
el.removeAttr('disabled');
311
}
312
}
313
@@ -316,11 +374,13 @@ $.fn.ajaxSubmit = function(options) {
316
id = 'jqFormIO' + (new Date().getTime());
317
if (s.iframeTarget) {
318
$io = $(s.iframeTarget);
319
- n = $io.attr('name');
320
- if (!n)
321
- $io.attr('name', id);
322
- else
323
id = n;
324
}
325
else {
326
$io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
@@ -347,17 +407,20 @@ $.fn.ajaxSubmit = function(options) {
347
if (io.contentWindow.document.execCommand) {
348
io.contentWindow.document.execCommand('Stop');
349
}
350
- }
351
catch(ignore) {}
352
353
$io.attr('src', s.iframeSrc); // abort op in progress
354
xhr.error = e;
355
- if (s.error)
356
s.error.call(s.context, xhr, e, status);
357
- if (g)
358
$.event.trigger("ajaxError", [xhr, s, e]);
359
- if (s.complete)
360
s.complete.call(s.context, xhr, e);
361
}
362
};
363
@@ -395,15 +458,44 @@ $.fn.ajaxSubmit = function(options) {
395
}
396
}
397
}
398
-
399
var CLIENT_TIMEOUT_ABORT = 1;
400
var SERVER_ABORT = 2;
401
-
402
function getDoc(frame) {
403
- var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
404
return doc;
405
}
406
-
407
// Rails CSRF hack (thanks to Yvan Barthelemy)
408
var csrf_token = $('meta[name=csrf-token]').attr('content');
409
var csrf_param = $('meta[name=csrf-param]').attr('content');
@@ -415,11 +507,14 @@ $.fn.ajaxSubmit = function(options) {
415
// take a breath so that pending repaints get some cpu time before the upload starts
416
function doSubmit() {
417
// make sure form attrs are set
418
- var t = $form.attr('target'), a = $form.attr('action');
419
420
// update form attrs in IE friendly way
421
form.setAttribute('target',id);
422
- if (!method) {
423
form.setAttribute('method', 'POST');
424
}
425
if (a != s.url) {
@@ -438,20 +533,22 @@ $.fn.ajaxSubmit = function(options) {
438
if (s.timeout) {
439
timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
440
}
441
-
442
// look for server aborts
443
function checkState() {
444
try {
445
var state = getDoc(io).readyState;
446
log('state = ' + state);
447
- if (state && state.toLowerCase() == 'uninitialized')
448
setTimeout(checkState,50);
449
}
450
catch(e) {
451
log('Server abort: ' , e, ' (', e.name, ')');
452
cb(SERVER_ABORT);
453
- if (timeoutHandle)
454
clearTimeout(timeoutHandle);
455
timeoutHandle = undefined;
456
}
457
}
@@ -479,17 +576,27 @@ $.fn.ajaxSubmit = function(options) {
479
if (!s.iframeTarget) {
480
// add iframe to doc and submit the form
481
$io.appendTo('body');
482
- if (io.attachEvent)
483
- io.attachEvent('onload', cb);
484
- else
485
- io.addEventListener('load', cb, false);
486
}
487
setTimeout(checkState,15);
488
- form.submit();
489
}
490
finally {
491
// reset attrs and remove "extra" input elements
492
form.setAttribute('action',a);
493
if(t) {
494
form.setAttribute('target', t);
495
} else {
@@ -512,11 +619,10 @@ $.fn.ajaxSubmit = function(options) {
512
if (xhr.aborted || callbackProcessed) {
513
return;
514
}
515
- try {
516
- doc = getDoc(io);
517
- }
518
- catch(ex) {
519
- log('cannot access response document: ', ex);
520
e = SERVER_ABORT;
521
}
522
if (e === CLIENT_TIMEOUT_ABORT && xhr) {
@@ -532,13 +638,16 @@ $.fn.ajaxSubmit = function(options) {
532
533
if (!doc || doc.location.href == s.iframeSrc) {
534
// response not received yet
535
- if (!timedOut)
536
return;
537
}
538
- if (io.detachEvent)
539
io.detachEvent('onload', cb);
540
- else
541
io.removeEventListener('load', cb, false);
542
543
var status = 'success', errMsg;
544
try {
@@ -565,11 +674,12 @@ $.fn.ajaxSubmit = function(options) {
565
var docRoot = doc.body ? doc.body : doc.documentElement;
566
xhr.responseText = docRoot ? docRoot.innerHTML : null;
567
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
568
- if (isXml)
569
s.dataType = 'xml';
570
xhr.getResponseHeader = function(header){
571
var headers = {'content-type': s.dataType};
572
- return headers[header];
573
};
574
// support for XHR 'status' & 'statusText' emulation :
575
if (docRoot) {
@@ -607,15 +717,15 @@ $.fn.ajaxSubmit = function(options) {
607
try {
608
data = httpData(xhr, dt, s);
609
}
610
- catch (e) {
611
status = 'parsererror';
612
- xhr.error = errMsg = (e || status);
613
}
614
}
615
- catch (e) {
616
- log('error caught: ',e);
617
status = 'error';
618
- xhr.error = errMsg = (e || status);
619
}
620
621
if (xhr.aborted) {
@@ -629,40 +739,52 @@ $.fn.ajaxSubmit = function(options) {
629
630
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
631
if (status === 'success') {
632
- if (s.success)
633
s.success.call(s.context, data, 'success', xhr);
634
deferred.resolve(xhr.responseText, 'success', xhr);
635
- if (g)
636
$.event.trigger("ajaxSuccess", [xhr, s]);
637
}
638
else if (status) {
639
- if (errMsg === undefined)
640
errMsg = xhr.statusText;
641
- if (s.error)
642
s.error.call(s.context, xhr, status, errMsg);
643
deferred.reject(xhr, 'error', errMsg);
644
- if (g)
645
$.event.trigger("ajaxError", [xhr, s, errMsg]);
646
}
647
648
- if (g)
649
$.event.trigger("ajaxComplete", [xhr, s]);
650
651
if (g && ! --$.active) {
652
$.event.trigger("ajaxStop");
653
}
654
655
- if (s.complete)
656
s.complete.call(s.context, xhr, status);
657
658
callbackProcessed = true;
659
- if (s.timeout)
660
clearTimeout(timeoutHandle);
661
662
// clean up
663
setTimeout(function() {
664
- if (!s.iframeTarget)
665
$io.remove();
666
xhr.responseXML = null;
667
}, 100);
668
}
@@ -690,8 +812,9 @@ $.fn.ajaxSubmit = function(options) {
690
data = xml ? xhr.responseXML : xhr.responseText;
691
692
if (xml && data.documentElement.nodeName === 'parsererror') {
693
- if ($.error)
694
$.error('parsererror');
695
}
696
if (s && s.dataFilter) {
697
data = s.dataFilter(data, type);
@@ -728,7 +851,7 @@ $.fn.ajaxSubmit = function(options) {
728
$.fn.ajaxForm = function(options) {
729
options = options || {};
730
options.delegation = options.delegation && $.isFunction($.fn.on);
731
-
732
// in jQuery 1.3+ we can fix mistakes with the ready state
733
if (!options.delegation && this.length === 0) {
734
var o = { s: this.selector, c: this.context };
@@ -758,16 +881,16 @@ $.fn.ajaxForm = function(options) {
758
.bind('click.form-plugin', options, captureSubmittingElement);
759
};
760
761
- // private event handlers
762
function doAjaxSubmit(e) {
763
/*jshint validthis:true */
764
var options = e.data;
765
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
766
e.preventDefault();
767
- $(this).ajaxSubmit(options);
768
}
769
}
770
-
771
function captureSubmittingElement(e) {
772
/*jshint validthis:true */
773
var target = e.target;
@@ -823,8 +946,23 @@ $.fn.formToArray = function(semantic, elements) {
823
}
824
825
var form = this[0];
826
var els = semantic ? form.getElementsByTagName('*') : form.elements;
827
- if (!els) {
828
return a;
829
}
830
@@ -832,13 +970,13 @@ $.fn.formToArray = function(semantic, elements) {
832
for(i=0, max=els.length; i < max; i++) {
833
el = els[i];
834
n = el.name;
835
- if (!n) {
836
continue;
837
}
838
839
if (semantic && form.clk && el.type == "image") {
840
// handle image inputs on the fly when semantic == true
841
- if(!el.disabled && form.clk == el) {
842
a.push({name: n, value: $(el).val(), type: el.type });
843
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
844
}
@@ -847,15 +985,17 @@ $.fn.formToArray = function(semantic, elements) {
847
848
v = $.fieldValue(el, true);
849
if (v && v.constructor == Array) {
850
- if (elements)
851
elements.push(el);
852
for(j=0, jmax=v.length; j < jmax; j++) {
853
a.push({name: n, value: v[j]});
854
}
855
}
856
- else if (feature.fileapi && el.type == 'file' && !el.disabled) {
857
- if (elements)
858
elements.push(el);
859
var files = el.files;
860
if (files.length) {
861
for (j=0; j < files.length; j++) {
@@ -868,8 +1008,9 @@ $.fn.formToArray = function(semantic, elements) {
868
}
869
}
870
else if (v !== null && typeof v != 'undefined') {
871
- if (elements)
872
elements.push(el);
873
a.push({name: n, value: v, type: el.type, required: el.required});
874
}
875
}
@@ -965,10 +1106,12 @@ $.fn.fieldValue = function(successful) {
965
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
966
continue;
967
}
968
- if (v.constructor == Array)
969
$.merge(val, v);
970
- else
971
val.push(v);
972
}
973
return val;
974
};
@@ -1002,7 +1145,7 @@ $.fieldValue = function(el, successful) {
1002
if (op.selected) {
1003
var v = op.value;
1004
if (!v) { // extra pain for IE...
1005
- v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
1006
}
1007
if (one) {
1008
return v;
@@ -1045,21 +1188,22 @@ $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
1045
else if (tag == 'select') {
1046
this.selectedIndex = -1;
1047
}
1048
- else if (t == "file") {
1049
- if ($.browser.msie) {
1050
- $(this).replaceWith($(this).clone());
1051
- } else {
1052
- $(this).val('');
1053
- }
1054
- }
1055
else if (includeHidden) {
1056
// includeHidden can be the value true, or it can be a selector string
1057
// indicating a special test; for example:
1058
// $('#myForm').clearForm('.special:hidden')
1059
// the above would clean hidden inputs that have the class of 'special'
1060
if ( (includeHidden === true && /hidden/.test(t)) ||
1061
- (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
1062
this.value = '';
1063
}
1064
});
1065
};
@@ -1118,8 +1262,9 @@ $.fn.ajaxSubmit.debug = false;
1118
1119
// helper fn for console logging
1120
function log() {
1121
- if (!$.fn.ajaxSubmit.debug)
1122
return;
1123
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1124
if (window.console && window.console.log) {
1125
window.console.log(msg);
@@ -1129,4 +1274,4 @@ function log() {
1129
}
1130
}
1131
1132
- })(jQuery);
1
/*!
2
* jQuery Form Plugin
3
+ * version: 3.51.0-2014.06.20
4
+ * Requires jQuery v1.5 or later
5
+ * Copyright (c) 2014 M. Alsup
6
* Examples and documentation at: http://malsup.com/jquery/form/
7
* Project repository: https://github.com/malsup/form
8
+ * Dual licensed under the MIT and GPL licenses.
9
+ * https://github.com/malsup/form#copyright-and-license
10
*/
11
+ /*global ActiveXObject */
12
+
13
+ // AMD support
14
+ (function (factory) {
15
+ "use strict";
16
+ if (typeof define === 'function' && define.amd) {
17
+ // using AMD; register as anon module
18
+ define(['jquery'], factory);
19
+ } else {
20
+ // no AMD; invoke directly
21
+ factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto );
22
+ }
23
+ }
24
+
25
+ (function($) {
26
"use strict";
27
28
/*
49
target: '#output'
50
});
51
});
52
+
53
You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
54
form does not have to exist when you invoke ajaxForm:
55
57
delegation: true,
58
target: '#output'
59
});
60
+
61
When using ajaxForm, the ajaxSubmit function will be invoked for you
62
at the appropriate time.
63
*/
69
feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
70
feature.formdata = window.FormData !== undefined;
71
72
+ var hasProp = !!$.fn.prop;
73
+
74
+ // attr2 uses prop when it can but checks the return type for
75
+ // an expected string. this accounts for the case where a form
76
+ // contains inputs with names like "action" or "method"; in those
77
+ // cases "prop" returns the element
78
+ $.fn.attr2 = function() {
79
+ if ( ! hasProp ) {
80
+ return this.attr.apply(this, arguments);
81
+ }
82
+ var val = this.prop.apply(this, arguments);
83
+ if ( ( val && val.jquery ) || typeof val === 'string' ) {
84
+ return val;
85
+ }
86
+ return this.attr.apply(this, arguments);
87
+ };
88
+
89
/**
90
* ajaxSubmit() provides a mechanism for immediately submitting
91
* an HTML form using AJAX.
98
log('ajaxSubmit: skipping submit process - no element selected');
99
return this;
100
}
101
+
102
var method, action, url, $form = this;
103
104
if (typeof options == 'function') {
105
options = { success: options };
106
}
107
+ else if ( options === undefined ) {
108
+ options = {};
109
+ }
110
+
111
+ method = options.type || this.attr2('method');
112
+ action = options.url || this.attr2('action');
113
114
url = (typeof action === 'string') ? $.trim(action) : '';
115
url = url || window.location.href || '';
116
if (url) {
121
options = $.extend(true, {
122
url: url,
123
success: $.ajaxSettings.success,
124
+ type: method || $.ajaxSettings.type,
125
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
126
}, options);
127
144
if ( traditional === undefined ) {
145
traditional = $.ajaxSettings.traditional;
146
}
147
+
148
var elements = [];
149
var qx, a = this.formToArray(options.semantic, elements);
150
if (options.data) {
168
var q = $.param(a, traditional);
169
if (qx) {
170
q = ( q ? (q + '&' + qx) : qx );
171
+ }
172
if (options.type.toUpperCase() == 'GET') {
173
options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
174
options.data = null; // data is null for 'get'
198
}
199
200
options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
201
+ var context = options.context || this ; // jQuery 1.4+ supports scope context
202
for (var i=0, max=callbacks.length; i < max; i++) {
203
callbacks[i].apply(context, [data, status, xhr || $form, $form]);
204
}
205
};
206
207
+ if (options.error) {
208
+ var oldError = options.error;
209
+ options.error = function(xhr, status, error) {
210
+ var context = options.context || this;
211
+ oldError.apply(context, [xhr, status, error, $form]);
212
+ };
213
+ }
214
+
215
+ if (options.complete) {
216
+ var oldComplete = options.complete;
217
+ options.complete = function(xhr, status) {
218
+ var context = options.context || this;
219
+ oldComplete.apply(context, [xhr, status, $form]);
220
+ };
221
+ }
222
+
223
// are there files to upload?
224
225
// [value] (issue #113), also see comment:
226
// https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219
227
+ var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; });
228
229
var hasFileInputs = fileInputs.length > 0;
230
var mp = 'multipart/form-data';
260
$form.removeData('jqxhr').data('jqxhr', jqxhr);
261
262
// clear element array
263
+ for (var k=0; k < elements.length; k++) {
264
elements[k] = null;
265
+ }
266
267
// fire 'notify' event
268
this.trigger('form-submit-notify', [this, options]);
270
271
// utility fn for deep serialization
272
function deepSerialize(extraData){
273
+ var serialized = $.param(extraData, options.traditional).split('&');
274
var len = serialized.length;
275
+ var result = [];
276
var i, part;
277
for (i=0; i < len; i++) {
278
// #252; undo param space replacement
279
serialized[i] = serialized[i].replace(/\+/g,' ');
280
part = serialized[i].split('=');
281
+ // #278; use array instead of object storage, favoring array serializations
282
+ result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]);
283
}
284
return result;
285
}
294
295
if (options.extraData) {
296
var serializedData = deepSerialize(options.extraData);
297
+ for (i=0; i < serializedData.length; i++) {
298
+ if (serializedData[i]) {
299
+ formdata.append(serializedData[i][0], serializedData[i][1]);
300
+ }
301
+ }
302
}
303
304
options.data = null;
309
cache: false,
310
type: method || 'POST'
311
});
312
+
313
if (options.uploadProgress) {
314
// workaround because jqXHR does not expose upload property
315
s.xhr = function() {
316
+ var xhr = $.ajaxSettings.xhr();
317
if (xhr.upload) {
318
+ xhr.upload.addEventListener('progress', function(event) {
319
var percent = 0;
320
var position = event.loaded || event.position; /*event.position is deprecated*/
321
var total = event.total;
323
percent = Math.ceil(position / total * 100);
324
}
325
options.uploadProgress(event, position, total, percent);
326
+ }, false);
327
}
328
return xhr;
329
};
330
}
331
332
s.data = null;
333
+ var beforeSend = s.beforeSend;
334
+ s.beforeSend = function(xhr, o) {
335
+ //Send FormData() provided by user
336
+ if (options.formData) {
337
+ o.data = options.formData;
338
+ }
339
+ else {
340
o.data = formdata;
341
+ }
342
+ if(beforeSend) {
343
+ beforeSend.call(this, xhr, o);
344
+ }
345
};
346
return $.ajax(s);
347
}
349
// private function for handling file uploads (hat tip to YAHOO!)
350
function fileUploadIframe(a) {
351
var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
352
var deferred = $.Deferred();
353
354
+ // #341
355
+ deferred.abort = function(status) {
356
+ xhr.abort(status);
357
+ };
358
+
359
if (a) {
360
// ensure that every serialized input is still enabled
361
for (i=0; i < elements.length; i++) {
362
el = $(elements[i]);
363
+ if ( hasProp ) {
364
el.prop('disabled', false);
365
+ }
366
+ else {
367
el.removeAttr('disabled');
368
+ }
369
}
370
}
371
374
id = 'jqFormIO' + (new Date().getTime());
375
if (s.iframeTarget) {
376
$io = $(s.iframeTarget);
377
+ n = $io.attr2('name');
378
+ if (!n) {
379
+ $io.attr2('name', id);
380
+ }
381
+ else {
382
id = n;
383
+ }
384
}
385
else {
386
$io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
407
if (io.contentWindow.document.execCommand) {
408
io.contentWindow.document.execCommand('Stop');
409
}
410
+ }
411
catch(ignore) {}
412
413
$io.attr('src', s.iframeSrc); // abort op in progress
414
xhr.error = e;
415
+ if (s.error) {
416
s.error.call(s.context, xhr, e, status);
417
+ }
418
+ if (g) {
419
$.event.trigger("ajaxError", [xhr, s, e]);
420
+ }
421
+ if (s.complete) {
422
s.complete.call(s.context, xhr, e);
423
+ }
424
}
425
};
426
458
}
459
}
460
}
461
+
462
var CLIENT_TIMEOUT_ABORT = 1;
463
var SERVER_ABORT = 2;
464
+
465
function getDoc(frame) {
466
+ /* it looks like contentWindow or contentDocument do not
467
+ * carry the protocol property in ie8, when running under ssl
468
+ * frame.document is the only valid response document, since
469
+ * the protocol is know but not on the other two objects. strange?
470
+ * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy
471
+ */
472
+
473
+ var doc = null;
474
+
475
+ // IE8 cascading access check
476
+ try {
477
+ if (frame.contentWindow) {
478
+ doc = frame.contentWindow.document;
479
+ }
480
+ } catch(err) {
481
+ // IE8 access denied under ssl & missing protocol
482
+ log('cannot get iframe.contentWindow document: ' + err);
483
+ }
484
+
485
+ if (doc) { // successful getting content
486
+ return doc;
487
+ }
488
+
489
+ try { // simply checking may throw in ie8 under ssl or mismatched protocol
490
+ doc = frame.contentDocument ? frame.contentDocument : frame.document;
491
+ } catch(err) {
492
+ // last attempt
493
+ log('cannot get iframe.contentDocument: ' + err);
494
+ doc = frame.document;
495
+ }
496
return doc;
497
}
498
+
499
// Rails CSRF hack (thanks to Yvan Barthelemy)
500
var csrf_token = $('meta[name=csrf-token]').attr('content');
501
var csrf_param = $('meta[name=csrf-param]').attr('content');
507
// take a breath so that pending repaints get some cpu time before the upload starts
508
function doSubmit() {
509
// make sure form attrs are set
510
+ var t = $form.attr2('target'),
511
+ a = $form.attr2('action'),
512
+ mp = 'multipart/form-data',
513
+ et = $form.attr('enctype') || $form.attr('encoding') || mp;
514
515
// update form attrs in IE friendly way
516
form.setAttribute('target',id);
517
+ if (!method || /post/i.test(method) ) {
518
form.setAttribute('method', 'POST');
519
}
520
if (a != s.url) {
533
if (s.timeout) {
534
timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
535
}
536
+
537
// look for server aborts
538
function checkState() {
539
try {
540
var state = getDoc(io).readyState;
541
log('state = ' + state);
542
+ if (state && state.toLowerCase() == 'uninitialized') {
543
setTimeout(checkState,50);
544
+ }
545
}
546
catch(e) {
547
log('Server abort: ' , e, ' (', e.name, ')');
548
cb(SERVER_ABORT);
549
+ if (timeoutHandle) {
550
clearTimeout(timeoutHandle);
551
+ }
552
timeoutHandle = undefined;
553
}
554
}
576
if (!s.iframeTarget) {
577
// add iframe to doc and submit the form
578
$io.appendTo('body');
579
+ }
580
+ if (io.attachEvent) {
581
+ io.attachEvent('onload', cb);
582
+ }
583
+ else {
584
+ io.addEventListener('load', cb, false);
585
}
586
setTimeout(checkState,15);
587
+
588
+ try {
589
+ form.submit();
590
+ } catch(err) {
591
+ // just in case form has element with name/id of 'submit'
592
+ var submitFn = document.createElement('form').submit;
593
+ submitFn.apply(form);
594
+ }
595
}
596
finally {
597
// reset attrs and remove "extra" input elements
598
form.setAttribute('action',a);
599
+ form.setAttribute('enctype', et); // #380
600
if(t) {
601
form.setAttribute('target', t);
602
} else {
619
if (xhr.aborted || callbackProcessed) {
620
return;
621
}
622
+
623
+ doc = getDoc(io);
624
+ if(!doc) {
625
+ log('cannot access response document');
626
e = SERVER_ABORT;
627
}
628
if (e === CLIENT_TIMEOUT_ABORT && xhr) {
638
639
if (!doc || doc.location.href == s.iframeSrc) {
640
// response not received yet
641
+ if (!timedOut) {
642
return;
643
+ }
644
}
645
+ if (io.detachEvent) {
646
io.detachEvent('onload', cb);
647
+ }
648
+ else {
649
io.removeEventListener('load', cb, false);
650
+ }
651
652
var status = 'success', errMsg;
653
try {
674
var docRoot = doc.body ? doc.body : doc.documentElement;
675
xhr.responseText = docRoot ? docRoot.innerHTML : null;
676
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
677
+ if (isXml) {
678
s.dataType = 'xml';
679
+ }
680
xhr.getResponseHeader = function(header){
681
var headers = {'content-type': s.dataType};
682
+ return headers[header.toLowerCase()];
683
};
684
// support for XHR 'status' & 'statusText' emulation :
685
if (docRoot) {
717
try {
718
data = httpData(xhr, dt, s);
719
}
720
+ catch (err) {
721
status = 'parsererror';
722
+ xhr.error = errMsg = (err || status);
723
}
724
}
725
+ catch (err) {
726
+ log('error caught: ',err);
727
status = 'error';
728
+ xhr.error = errMsg = (err || status);
729
}
730
731
if (xhr.aborted) {
739
740
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
741
if (status === 'success') {
742
+ if (s.success) {
743
s.success.call(s.context, data, 'success', xhr);
744
+ }
745
deferred.resolve(xhr.responseText, 'success', xhr);
746
+ if (g) {
747
$.event.trigger("ajaxSuccess", [xhr, s]);
748
+ }
749
}
750
else if (status) {
751
+ if (errMsg === undefined) {
752
errMsg = xhr.statusText;
753
+ }
754
+ if (s.error) {
755
s.error.call(s.context, xhr, status, errMsg);
756
+ }
757
deferred.reject(xhr, 'error', errMsg);
758
+ if (g) {
759
$.event.trigger("ajaxError", [xhr, s, errMsg]);
760
+ }
761
}
762
763
+ if (g) {
764
$.event.trigger("ajaxComplete", [xhr, s]);
765
+ }
766
767
if (g && ! --$.active) {
768
$.event.trigger("ajaxStop");
769
}
770
771
+ if (s.complete) {
772
s.complete.call(s.context, xhr, status);
773
+ }
774
775
callbackProcessed = true;
776
+ if (s.timeout) {
777
clearTimeout(timeoutHandle);
778
+ }
779
780
// clean up
781
setTimeout(function() {
782
+ if (!s.iframeTarget) {
783
$io.remove();
784
+ }
785
+ else { //adding else to clean up existing iframe response.
786
+ $io.attr('src', s.iframeSrc);
787
+ }
788
xhr.responseXML = null;
789
}, 100);
790
}
812
data = xml ? xhr.responseXML : xhr.responseText;
813
814
if (xml && data.documentElement.nodeName === 'parsererror') {
815
+ if ($.error) {
816
$.error('parsererror');
817
+ }
818
}
819
if (s && s.dataFilter) {
820
data = s.dataFilter(data, type);
851
$.fn.ajaxForm = function(options) {
852
options = options || {};
853
options.delegation = options.delegation && $.isFunction($.fn.on);
854
+
855
// in jQuery 1.3+ we can fix mistakes with the ready state
856
if (!options.delegation && this.length === 0) {
857
var o = { s: this.selector, c: this.context };
881
.bind('click.form-plugin', options, captureSubmittingElement);
882
};
883
884
+ // private event handlers
885
function doAjaxSubmit(e) {
886
/*jshint validthis:true */
887
var options = e.data;
888
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
889
e.preventDefault();
890
+ $(e.target).ajaxSubmit(options); // #365
891
}
892
}
893
+
894
function captureSubmittingElement(e) {
895
/*jshint validthis:true */
896
var target = e.target;
946
}
947
948
var form = this[0];
949
+ var formId = this.attr('id');
950
var els = semantic ? form.getElementsByTagName('*') : form.elements;
951
+ var els2;
952
+
953
+ if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390
954
+ els = $(els).get(); // convert to standard array
955
+ }
956
+
957
+ // #386; account for inputs outside the form which use the 'form' attribute
958
+ if ( formId ) {
959
+ els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet
960
+ if ( els2.length ) {
961
+ els = (els || []).concat(els2);
962
+ }
963
+ }
964
+
965
+ if (!els || !els.length) {
966
return a;
967
}
968
970
for(i=0, max=els.length; i < max; i++) {
971
el = els[i];
972
n = el.name;
973
+ if (!n || el.disabled) {
974
continue;
975
}
976
977
if (semantic && form.clk && el.type == "image") {
978
// handle image inputs on the fly when semantic == true
979
+ if(form.clk == el) {
980
a.push({name: n, value: $(el).val(), type: el.type });
981
a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
982
}
985
986
v = $.fieldValue(el, true);
987
if (v && v.constructor == Array) {
988
+ if (elements) {
989
elements.push(el);
990
+ }
991
for(j=0, jmax=v.length; j < jmax; j++) {
992
a.push({name: n, value: v[j]});
993
}
994
}
995
+ else if (feature.fileapi && el.type == 'file') {
996
+ if (elements) {
997
elements.push(el);
998
+ }
999
var files = el.files;
1000
if (files.length) {
1001
for (j=0; j < files.length; j++) {
1008
}
1009
}
1010
else if (v !== null && typeof v != 'undefined') {
1011
+ if (elements) {
1012
elements.push(el);
1013
+ }
1014
a.push({name: n, value: v, type: el.type, required: el.required});
1015
}
1016
}
1106
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
1107
continue;
1108
}
1109
+ if (v.constructor == Array) {
1110
$.merge(val, v);
1111
+ }
1112
+ else {
1113
val.push(v);
1114
+ }
1115
}
1116
return val;
1117
};
1145
if (op.selected) {
1146
var v = op.value;
1147
if (!v) { // extra pain for IE...
1148
+ v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value;
1149
}
1150
if (one) {
1151
return v;
1188
else if (tag == 'select') {
1189
this.selectedIndex = -1;
1190
}
1191
+ else if (t == "file") {
1192
+ if (/MSIE/.test(navigator.userAgent)) {
1193
+ $(this).replaceWith($(this).clone(true));
1194
+ } else {
1195
+ $(this).val('');
1196
+ }
1197
+ }
1198
else if (includeHidden) {
1199
// includeHidden can be the value true, or it can be a selector string
1200
// indicating a special test; for example:
1201
// $('#myForm').clearForm('.special:hidden')
1202
// the above would clean hidden inputs that have the class of 'special'
1203
if ( (includeHidden === true && /hidden/.test(t)) ||
1204
+ (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) {
1205
this.value = '';
1206
+ }
1207
}
1208
});
1209
};
1262
1263
// helper fn for console logging
1264
function log() {
1265
+ if (!$.fn.ajaxSubmit.debug) {
1266
return;
1267
+ }
1268
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1269
if (window.console && window.console.log) {
1270
window.console.log(msg);
1274
}
1275
}
1276
1277
+ }));
js/jqueryui.d.ts ADDED
@@ -0,0 +1,1843 @@