PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.0.5
Tutor LMS – eLearning and online course solution v2.0.5
3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / Assets.php
tutor / classes Last commit date
Addons.php 4 years ago Admin.php 4 years ago Ajax.php 4 years ago Announcements.php 4 years ago Assets.php 4 years ago Backend_Page_Trait.php 4 years ago Course.php 4 years ago Course_Filter.php 4 years ago Course_List.php 4 years ago Course_Settings_Tabs.php 4 years ago Course_Widget.php 4 years ago Custom_Validation.php 4 years ago Dashboard.php 4 years ago FormHandler.php 4 years ago Frontend.php 4 years ago Gutenberg.php 4 years ago Input.php 4 years ago Instructor.php 4 years ago Instructors_List.php 4 years ago Lesson.php 4 years ago Options_V2.php 4 years ago Post_types.php 4 years ago Private_Course_Access.php 4 years ago Q_and_A.php 4 years ago Question_Answers_List.php 4 years ago Quiz.php 4 years ago Quiz_Attempts_List.php 4 years ago RestAPI.php 4 years ago Reviews.php 4 years ago Rewrite_Rules.php 4 years ago Shortcode.php 4 years ago Student.php 4 years ago Students_List.php 4 years ago Taxonomies.php 4 years ago Template.php 4 years ago Theme_Compatibility.php 5 years ago Tools.php 4 years ago Tools_V2.php 4 years ago Tutor.php 4 years ago TutorEDD.php 4 years ago Tutor_Base.php 5 years ago Tutor_List_Table.php 4 years ago Tutor_Setup.php 4 years ago Upgrader.php 4 years ago User.php 4 years ago Utils.php 4 years ago Video_Stream.php 4 years ago Withdraw.php 4 years ago Withdraw_Requests_List.php 4 years ago WooCommerce.php 4 years ago
Assets.php
522 lines
1 <?php
2
3 namespace TUTOR;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 class Assets {
10
11 public function __construct()
12 {
13 /**
14 * Common scripts loading
15 */
16 add_action('admin_enqueue_scripts', array($this, 'common_scripts'));
17 add_action('wp_enqueue_scripts', array($this, 'common_scripts'));
18 /**
19 * Front and backend script enqueue
20 */
21 add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
22 add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) );
23
24 add_action('admin_enqueue_scripts', array($this, 'load_meta_data'));
25 add_action('wp_enqueue_scripts', array($this, 'load_meta_data'));
26
27 /**
28 * Text domain loading
29 */
30 add_action( 'admin_enqueue_scripts', array( $this, 'tutor_script_text_domain' ), 100 );
31 add_action( 'wp_enqueue_scripts', array( $this, 'tutor_script_text_domain' ), 100 );
32 add_filter( 'tutor_localize_data', array( $this, 'modify_localize_data' ) );
33
34 /**
35 * register translatable function to load
36 * handled script with text domain attached to
37 *
38 * @since 1.9.0
39 */
40 add_action( 'admin_head', array( $this, 'tutor_add_mce_button' ) );
41 add_filter( 'get_the_generator_html', array( $this, 'tutor_generator_tag' ), 10, 2 );
42 add_filter( 'get_the_generator_xhtml', array( $this, 'tutor_generator_tag' ), 10, 2 );
43
44 /**
45 * Add translation support for external tinyMCE button
46 *
47 * @since 1.9.7
48 */
49 add_filter('mce_external_languages', array($this, 'tutor_tinymce_translate'));
50
51 /**
52 * Identifier class to body tag
53 *
54 * @since v1.9.9
55 */
56 add_filter( 'body_class', array( $this, 'add_identifier_class_to_body' ) );
57 add_filter( 'admin_body_class', array( $this, 'add_identifier_class_to_body' ) );
58
59 /**
60 * Add edit with front end builder button on Gutenberg editor
61 *
62 * @since v2.0.5
63 */
64 add_action( 'enqueue_block_editor_assets', __CLASS__ . '::add_frontend_editor_button' );
65 }
66
67 private function get_default_localized_data()
68 {
69 global $wp_version, $wp_query;
70
71 $home_url = get_home_url();
72 $parsed = parse_url($home_url);
73
74 $base_path = (is_array($parsed) && isset($parsed['path'])) ? $parsed['path'] : '/';
75 $base_path = rtrim($base_path, '/') . '/';
76
77 $post_id = get_the_ID();
78 $post_type = get_post_type( $post_id );
79
80 $query_vars = $wp_query->query_vars;
81 $current_page = isset( $query_vars['tutor_dashboard_page'] ) ? $query_vars['tutor_dashboard_page'] : '';
82
83 return array(
84 'ajaxurl' => admin_url( 'admin-ajax.php' ),
85 'home_url' => get_home_url(),
86 'site_title' => get_bloginfo( 'title' ),
87 'base_path' => tutor()->basepath,
88 'tutor_url' => tutor()->url,
89 'tutor_pro_url' => function_exists( 'tutor_pro' ) ? tutor_pro()->url : null,
90 'nonce_key' => tutor()->nonce,
91 tutor()->nonce => wp_create_nonce( tutor()->nonce_action ),
92 'loading_icon_url' => get_admin_url() . 'images/wpspin_light.gif',
93 'placeholder_img_src' => tutor_placeholder_img_src(),
94 'enable_lesson_classic_editor' => get_tutor_option( 'enable_lesson_classic_editor' ),
95 'tutor_frontend_dashboard_url' => tutor_utils()->get_tutor_dashboard_page_permalink(),
96 'wp_date_format' => tutor_js_date_format_against_wp(),
97 'is_admin' => is_admin(),
98 'is_admin_bar_showing' => is_admin_bar_showing(),
99 'addons_data' => tutor_utils()->prepare_free_addons_data(),
100 'current_user' => wp_get_current_user(),
101 'content_change_event' => 'tutor_content_changed_event',
102 'is_tutor_course_edit' => isset( $_GET[ 'action'] ) && 'edit' === $_GET['action'] && tutor()->course_post_type === get_post_type( get_the_ID() ) ? true : false,
103 'assignment_max_file_allowed' => 'tutor_assignments' === $post_type ? (int) tutor_utils()->get_assignment_option( $post_id, 'upload_files_limit' ) : 0,
104 'current_page' => $current_page,
105 );
106 }
107
108 public function admin_scripts()
109 {
110 wp_enqueue_style('tutor-select2', tutor()->url . 'assets/packages/select2/select2.min.css', array(), TUTOR_VERSION);
111 wp_enqueue_style('tutor-admin', tutor()->url . 'assets/css/tutor-admin.min.css', array(), TUTOR_VERSION);
112 /**
113 * Scripts
114 */
115 wp_enqueue_media();
116
117 wp_enqueue_script('wp-color-picker');
118 wp_enqueue_style('wp-color-picker');
119
120 wp_enqueue_script( 'jquery-ui-slider' );
121 wp_enqueue_script( 'jquery-ui-datepicker' );
122
123 wp_enqueue_script('tutor-select2', tutor()->url . 'assets/packages/select2/select2.full.min.js', array('jquery'), TUTOR_VERSION, true);
124 wp_enqueue_script('tutor-admin', tutor()->url . 'assets/js/tutor-admin.min.js', array('jquery', 'wp-color-picker', 'wp-i18n'), TUTOR_VERSION, true);
125 }
126
127 /**
128 * Load frontend scripts
129 */
130 public function frontend_scripts()
131 {
132 global $post, $wp_query;
133
134 /**
135 * We checked wp_enqueue_editor() in condition because it conflicting with Divi Builder
136 * condition updated @since v.1.7.4
137 */
138
139 if ( is_single() ) {
140 if ( function_exists( 'et_pb_is_pagebuilder_used' ) ) {
141 $is_page_builder_used = et_pb_is_pagebuilder_used( get_the_ID() );
142 if ( ! $is_page_builder_used ) {
143 wp_enqueue_editor();
144 }
145 } else {
146 wp_enqueue_editor();
147 }
148 }
149
150 /**
151 * Initializing quicktags script to use in wp_editor();
152 */
153 wp_enqueue_script( 'quicktags' );
154
155 $tutor_dashboard_page_id = (int) tutor_utils()->get_option( 'tutor_dashboard_page_id' );
156 if ( $tutor_dashboard_page_id === get_the_ID() ) {
157 wp_enqueue_media();
158 }
159
160 /**
161 * Enabling Sorting, draggable, droppable...
162 */
163 wp_enqueue_script( 'jquery-ui-sortable' );
164
165 // Plyr
166 if(is_single_course(true)) {
167 wp_enqueue_style('tutor-plyr', tutor()->url . 'assets/packages/plyr/plyr.css', array(), TUTOR_VERSION);
168 wp_enqueue_script('tutor-plyr', tutor()->url . 'assets/packages/plyr/plyr.polyfilled.min.js', array('jquery'), TUTOR_VERSION, true);
169 }
170
171 // Social Share
172 wp_enqueue_script('tutor-social-share', tutor()->url . 'assets/packages/SocialShare/SocialShare.min.js', array('jquery'), TUTOR_VERSION, true);
173
174 /**
175 * Chart Data
176 */
177 if (!empty($wp_query->query_vars['tutor_dashboard_page'])) {
178 wp_enqueue_script('jquery-ui-slider');
179
180 wp_enqueue_style('tutor-select2', tutor()->url . 'assets/packages/select2/select2.min.css', array(), TUTOR_VERSION);
181 wp_enqueue_script('tutor-select2', tutor()->url . 'assets/packages/select2/select2.full.min.js', array('jquery'), TUTOR_VERSION, true);
182
183 if ($wp_query->query_vars['tutor_dashboard_page'] === 'earning') {
184 wp_enqueue_script('tutor-front-chart-js', tutor()->url . 'assets/js/lib/Chart.bundle.min.js', array(), TUTOR_VERSION);
185 wp_enqueue_script('jquery-ui-datepicker');
186 }
187 }
188 /**
189 * dependency wp-i18n added for
190 * translate js file
191 *
192 * @since 1.9.0
193 */
194 wp_enqueue_style('tutor-frontend', tutor()->url . 'assets/css/tutor-front.min.css', array(), TUTOR_VERSION);
195 wp_enqueue_script('tutor-frontend', tutor()->url . 'assets/js/tutor-front.min.js', array('jquery', 'wp-i18n'), TUTOR_VERSION, true );
196
197 /**
198 * Load frontend dashboard style
199 *
200 * @since v1.9.8
201 */
202 if (tutor_utils()->is_tutor_frontend_dashboard()) {
203 wp_enqueue_style('tutor-frontend-dashboard-css', tutor()->url . 'assets/css/tutor-frontend-dashboard.min.css', TUTOR_VERSION);
204 }
205
206 // Load date picker for announcement at frontend
207 wp_enqueue_script( 'jquery-ui-datepicker' );
208 }
209
210 public function modify_localize_data($localize_data)
211 {
212 global $post;
213
214 if ( is_admin() ) {
215 if ( ! empty( $_GET['taxonomy'] ) && ( $_GET['taxonomy'] === 'course-category' || $_GET['taxonomy'] === 'course-tag' ) ) {
216 $localize_data['open_tutor_admin_menu'] = true;
217 }
218 } else {
219
220 // Assign quiz option
221 if (!empty($post->post_type) && $post->post_type === 'tutor_quiz') {
222 $single_quiz_options = (array) tutor_utils()->get_quiz_option($post->ID);
223 $saved_quiz_options = array(
224 'quiz_when_time_expires' => tutor_utils()->get_option('quiz_when_time_expires'),
225 );
226
227 $quiz_options = array_merge($single_quiz_options, $saved_quiz_options);
228
229 $previous_attempts = tutor_utils()->quiz_attempts();
230
231 if ($previous_attempts && count($previous_attempts)) {
232 $quiz_options['quiz_auto_start'] = 0;
233 }
234
235 $localize_data['quiz_options'] = $quiz_options;
236 }
237
238 // Including player assets if video exists
239 if ( tutor_utils()->has_video_in_single() ) {
240 $localize_data['post_id'] = get_the_ID();
241 $localize_data['best_watch_time'] = 0;
242
243 $best_watch_time = tutor_utils()->get_lesson_reading_info( get_the_ID(), 0, 'video_best_watched_time' );
244 if ( $best_watch_time > 0 ) {
245 $localize_data['best_watch_time'] = $best_watch_time;
246 }
247 }
248 }
249
250 return $localize_data;
251 }
252
253 public function common_scripts() {
254
255 // Fonts
256 wp_enqueue_style('tutor-icon', tutor()->url . 'assets/css/tutor-icon.min.css', array(), TUTOR_VERSION);
257
258 // Common css library
259 if ( is_rtl() ) {
260 wp_enqueue_style('tutor', tutor()->url . 'assets/css/tutor.rtl.min.css', array(), TUTOR_VERSION);
261 } else {
262 wp_enqueue_style('tutor', tutor()->url . 'assets/css/tutor.min.css', array(), TUTOR_VERSION);
263 }
264
265 // Load course builder resources
266 if (tutor_utils()->get_course_builder_screen()) {
267 wp_enqueue_script('tutor-course-builder', tutor()->url . 'assets/js/tutor-course-builder.min.js', array('jquery', 'wp-i18n'), TUTOR_VERSION, true);
268 wp_enqueue_style('tutor-course-builder-css', tutor()->url . 'assets/css/tutor-course-builder.min.css', array(), TUTOR_VERSION);
269 }
270 /**
271 * Load tutor common scripts both backend and frontend
272 *
273 * @since v2.0.0
274 */
275 wp_enqueue_script('tutor-script', tutor()->url . 'assets/js/tutor.min.js', array('jquery', 'wp-i18n'), TUTOR_VERSION, true);
276 }
277
278 public function load_meta_data()
279 {
280 // Localize scripts
281 $localize_data = apply_filters('tutor_localize_data', $this->get_default_localized_data());
282 wp_localize_script('tutor-frontend', '_tutorobject', $localize_data);
283 wp_localize_script('tutor-admin', '_tutorobject', $localize_data);
284 wp_localize_script('tutor-course-builder', '_tutorobject', $localize_data);
285 wp_localize_script('tutor-script', '_tutorobject', $localize_data);
286
287 // Inline styles
288 wp_add_inline_style('tutor-frontend', $this->load_color_palette());
289 wp_add_inline_style('tutor-admin', $this->load_color_palette());
290 }
291
292 private function load_color_palette()
293 {
294 $colors = array(
295 'tutor_primary_color' => '--tutor-color-primary',
296 'tutor_primary_hover_color' => '--tutor-color-primary-hover',
297 'tutor_text_color' => '--tutor-body-color',
298 'tutor_border_color' => '--tutor-border-color',
299 'tutor_gray_color' => '--tutor-color-gray',
300 );
301
302 // admin colors
303 $admin_colors = [];
304 if (is_admin()) {
305 $admin_color = get_user_option( 'admin_color' );
306
307 switch ($admin_color) {
308 case 'light':
309 $admin_color_codes = ['#04a4cc', '#04b0db'];
310 break;
311
312 case 'modern':
313 $admin_color_codes = ['#3858e9', '#4664eb'];
314 break;
315
316 case 'blue':
317 $admin_color_codes = ['#e1a948', '#e3af55'];
318 break;
319
320 case 'coffee':
321 $admin_color_codes = ['#c7a589', '#ccad93'];
322 break;
323
324 case 'ectoplasm':
325 $admin_color_codes = ['#a3b745', '#a9bd4f'];
326 break;
327
328 case 'midnight':
329 $admin_color_codes = ['#e14d43', '#e35950'];
330 break;
331
332 case 'ocean':
333 $admin_color_codes = ['#9ebaa0', '#a7c0a9'];
334 break;
335
336 case 'sunrise':
337 $admin_color_codes = ['#dd823b', '#df8a48'];
338 break;
339
340 default:
341 $admin_color_codes = ['#007cba', '#006ba1'];
342 break;
343 }
344
345 $admin_colors = [
346 '--tutor-color-primary' => $admin_color_codes[0],
347 '--tutor-color-primary-hover' => $admin_color_codes[1],
348 ];
349 }
350
351 $fallback_colors = [
352 'tutor_primary_color' => '#3E64DE',
353 'tutor_primary_hover_color' => '#395BCA',
354 'tutor_text_color' => '#212327',
355 'tutor_border_color' => '#E3E5EB',
356 'tutor_gray_color' => '#CDCFD5'
357 ];
358
359 $color_string = '';
360 foreach ($colors as $key => $property) {
361 $fallback_color = isset( $fallback_colors[$key] ) ? $fallback_colors[$key] : '#212327';
362 $color = tutor_utils()->get_option( $key, $fallback_color );
363 $color_rgb = tutor_utils()->hex2rgb( $color );
364
365 if (is_admin() && isset($admin_colors[$property])) {
366 $color = $admin_colors[$property];
367 $color_rgb = tutor_utils()->hex2rgb( $admin_colors[$property] );
368 }
369
370 if ($color) {
371 $color_string .= $property . ':' . $color . ';';
372 }
373
374 if ($color_rgb) {
375 $color_string .= $property . '-rgb:' . $color_rgb . ';';
376 }
377 }
378
379 return ':root{' . $color_string . '}';
380 }
381
382 /**
383 * Add Tinymce button for placing shortcode
384 */
385 function tutor_add_mce_button()
386 {
387 // check user permissions
388 if (!current_user_can('edit_posts') && !current_user_can('edit_pages')) {
389 return;
390 }
391 // check if WYSIWYG is enabled
392 if ('true' == get_user_option('rich_editing')) {
393 add_filter('mce_external_plugins', array($this, 'tutor_add_tinymce_js'));
394 add_filter('mce_buttons', array($this, 'tutor_register_mce_button'));
395 }
396 }
397 // Declare script for new button
398 function tutor_add_tinymce_js($plugin_array)
399 {
400 $plugin_array['tutor_button'] = tutor()->url . 'assets/js/lib/mce-button.js';
401 return $plugin_array;
402 }
403 // Register new button in the editor
404 function tutor_register_mce_button($buttons)
405 {
406 array_push($buttons, 'tutor_button');
407 return $buttons;
408 }
409
410 /**
411 * Output generator tag to aid debugging.
412 *
413 * @param string $gen Generator.
414 * @param string $type Type.
415 * @return string
416 */
417 function tutor_generator_tag($gen, $type)
418 {
419 switch ($type) {
420 case 'html':
421 $gen .= "\n" . '<meta name="generator" content="TutorLMS ' . TUTOR_VERSION . '">';
422 break;
423 case 'xhtml':
424 $gen .= "\n" . '<meta name="generator" content="TutorLMS ' . TUTOR_VERSION . '" />';
425 break;
426 }
427 return $gen;
428 }
429
430 /**
431 * load text domain handled script after all enqueue_scripts
432 * registered functions
433 *
434 * @since 1.9.0
435 */
436 function tutor_script_text_domain() {
437 wp_set_script_translations( 'tutor-frontend', 'tutor', tutor()->path . 'languages/' );
438 wp_set_script_translations( 'tutor-admin', 'tutor', tutor()->path . 'languages/' );
439 wp_set_script_translations( 'tutor-course-builder', 'tutor', tutor()->path . 'languages/' );
440 }
441
442 /**
443 * Add translation support for external tinyMCE button
444 *
445 * @since 1.9.7
446 */
447 function tutor_tinymce_translate() {
448 $locales['tutor_button'] = tutor()->path . 'includes/tinymce_translate.php';
449 return $locales;
450 }
451
452 public function add_identifier_class_to_body($classes)
453 {
454 $course_builder_screen = tutor_utils()->get_course_builder_screen();
455 $to_add = array('tutor-lms');
456
457 // Add backend course editor identifier class to body
458 if ($course_builder_screen) {
459 $to_add[] = is_admin() ? 'tutor-backend' : '';
460 $to_add[] = ' tutor-screen-course-builder tutor-screen-course-builder-' . $course_builder_screen . ' ';
461 }
462
463 // Add frontend course builder identifier class
464 if (!$course_builder_screen && tutor_utils()->is_tutor_frontend_dashboard()) {
465 $to_add[] = 'tutor-screen-frontend-dashboard';
466 }
467
468 if (is_admin()) {
469 $screen = function_exists('get_current_screen') ? get_current_screen() : null;
470 $base = ($screen && is_object($screen) && property_exists($screen, 'base')) ? $screen->base : '';
471 $index = strpos($base, 'tutor');
472
473 if($index===0 || $index>0) {
474 $to_add[] = 'tutor-backend';
475
476 if (isset($_GET['page']) && $_GET['page'] == 'tutor_settings') {
477 $to_add[] = 'tutor-screen-backend-settings ';
478 }
479 if (isset($_GET['page'])) {
480 $to_add[] = 'tutor-backend-' . $_GET['page'];
481 }
482 }
483 }
484
485 // Remove duplicate classes if any
486 $to_add = array_unique($to_add);
487
488 if(is_array($classes)) {
489 $classes = array_merge($classes, $to_add);
490 } else {
491 $classes .= implode(' ', $to_add);
492 }
493
494 return $classes;
495 }
496
497 /**
498 * Enqueue script for adding edit with frontend course builder button
499 * on the Gutenberg editor
500 *
501 * @since v2.0.5
502 *
503 * @return void
504 */
505 public static function add_frontend_editor_button() {
506 $wp_screen = get_current_screen();
507
508 if ( is_a( $wp_screen, 'WP_Screen' ) && tutor()->course_post_type === $wp_screen->post_type ) {
509 wp_enqueue_script( 'tutor-gutenberg', tutor()->url . 'assets/js/tutor-gutenberg.min.js', array(), TUTOR_VERSION, true );
510 $data = array(
511 'frontend_dashboard_url' => esc_url( trailingslashit( tutor_utils()->tutor_dashboard_url( 'create-course' ) ) ) . '?course_ID=' . get_the_ID(),
512 );
513
514 wp_add_inline_script(
515 'tutor-gutenberg',
516 'const tutorInlineData =' . json_encode( $data ),
517 'before'
518 );
519 }
520 }
521 }
522