Yaffs site version 1.1
[yaffs-website] / web / modules / contrib / slick / slick_ui / src / Form / SlickFormBase.php
1 <?php
2
3 namespace Drupal\slick_ui\Form;
4
5 use Drupal\Core\Url;
6 use Drupal\Core\Entity\EntityForm;
7 use Drupal\Core\Form\FormStateInterface;
8 use Drupal\Core\Entity\EntityTypeInterface;
9 use Drupal\slick\Entity\Slick;
10 use Drupal\slick\Form\SlickAdminInterface;
11 use Drupal\slick\SlickManagerInterface;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13
14 /**
15  * Provides base form for a slick instance configuration form.
16  */
17 abstract class SlickFormBase extends EntityForm {
18
19   /**
20    * The slick admin service.
21    *
22    * @var \Drupal\slick\Form\SlickAdminInterface
23    */
24   protected $admin;
25
26   /**
27    * The slick manager service.
28    *
29    * @var \Drupal\slick\SlickManagerInterface
30    */
31   protected $manager;
32
33   /**
34    * The JS easing options.
35    *
36    * @var array
37    */
38   protected $jsEasingOptions;
39
40   /**
41    * Constructs a SlickForm object.
42    */
43   public function __construct(SlickAdminInterface $admin, SlickManagerInterface $manager) {
44     $this->admin = $admin;
45     $this->manager = $manager;
46   }
47
48   /**
49    * {@inheritdoc}
50    */
51   public static function create(ContainerInterface $container) {
52     return new static(
53       $container->get('slick.admin'),
54       $container->get('slick.manager')
55     );
56   }
57
58   /**
59    * Returns the slick admin service.
60    */
61   public function admin() {
62     return $this->admin;
63   }
64
65   /**
66    * Returns the slick manager service.
67    */
68   public function manager() {
69     return $this->manager;
70   }
71
72   /**
73    * {@inheritdoc}
74    */
75   public function form(array $form, FormStateInterface $form_state) {
76     // Change page title for the duplicate operation.
77     if ($this->operation == 'duplicate') {
78       $form['#title'] = $this->t('<em>Duplicate slick optionset</em>: @label', ['@label' => $this->entity->label()]);
79       $this->entity = $this->entity->createDuplicate();
80     }
81
82     // Change page title for the edit operation.
83     if ($this->operation == 'edit') {
84       $form['#title'] = $this->t('<em>Edit slick optionset</em>: @label', ['@label' => $this->entity->label()]);
85     }
86
87     $slick     = $this->entity;
88     $path      = drupal_get_path('module', 'slick');
89     $tooltip   = ['class' => ['is-tooltip']];
90     $readme    = Url::fromUri('base:' . $path . '/README.txt')->toString();
91     $admin_css = $this->manager->configLoad('admin_css', 'blazy.settings');
92
93     $form['#attributes']['class'][] = 'form--slick';
94     $form['#attributes']['class'][] = 'form--blazy';
95     $form['#attributes']['class'][] = 'form--optionset';
96
97     $form['label'] = [
98       '#type'          => 'textfield',
99       '#title'         => $this->t('Label'),
100       '#default_value' => $slick->label(),
101       '#maxlength'     => 255,
102       '#required'      => TRUE,
103       '#description'   => $this->t("Label for the Slick optionset."),
104       '#attributes'    => $tooltip,
105       '#prefix'        => '<div class="form__header form__half form__half--first has-tooltip clearfix">',
106     ];
107
108     // Keep the legacy CTools ID, i.e.: name as ID.
109     $form['name'] = [
110       '#type'          => 'machine_name',
111       '#default_value' => $slick->id(),
112       '#maxlength'     => EntityTypeInterface::BUNDLE_MAX_LENGTH,
113       '#machine_name'  => [
114         'source' => ['label'],
115         'exists' => '\Drupal\slick\Entity\Slick::load',
116       ],
117       '#attributes'    => $tooltip,
118       '#disabled'      => !$slick->isNew(),
119       '#suffix'        => '</div>',
120     ];
121
122     $form['skin'] = [
123       '#type'          => 'select',
124       '#title'         => $this->t('Skin'),
125       '#options'       => $this->admin->getSkinsByGroupOptions(),
126       '#empty_option'  => $this->t('- None -'),
127       '#default_value' => $slick->getSkin(),
128       '#description'   => $this->t('Skins allow swappable layouts like next/prev links, split image and caption, etc. However a combination of skins and options may lead to unpredictable layouts, get yourself dirty. See main <a href="@url">README</a> for details on Skins. Only useful for custom work, and ignored/overridden by slick formatters or sub-modules.', ['@url' => $readme]),
129       '#attributes'    => $tooltip,
130       '#prefix'        => '<div class="form__header form__half form__half--last has-tooltip clearfix">',
131     ];
132
133     $form['group'] = [
134       '#type'          => 'select',
135       '#title'         => $this->t('Group'),
136       '#options'       => [
137         'main'      => t('Main'),
138         'overlay'   => t('Overlay'),
139         'thumbnail' => t('Thumbnail'),
140       ],
141       '#empty_option'  => $this->t('- None -'),
142       '#default_value' => $slick->getGroup(),
143       '#description'   => $this->t('Group this optionset to avoid confusion for optionset selections. Leave empty to make it available for all.'),
144       '#attributes'    => $tooltip,
145     ];
146
147     $form['breakpoints'] = [
148       '#title'         => $this->t('Breakpoints'),
149       '#type'          => 'textfield',
150       '#default_value' => $form_state->hasValue('breakpoints') ? $form_state->getValue('breakpoints') : $slick->getBreakpoints(),
151       '#description'   => $this->t('The number of breakpoints added to Responsive display, max 9. This is not Breakpoint Width (480px, etc).'),
152       '#ajax' => [
153         'callback' => '::addBreakpoints',
154         'wrapper'  => 'edit-breakpoints-ajax-wrapper',
155         'event'    => 'change',
156         'progress' => ['type' => 'fullscreen'],
157         'effect'   => 'fade',
158         'speed'    => 'fast',
159       ],
160       '#attributes' => $tooltip,
161       '#maxlength'  => 1,
162     ];
163
164     $form['optimized'] = [
165       '#type'          => 'checkbox',
166       '#title'         => $this->t('Optimized'),
167       '#default_value' => $slick->optimized(),
168       '#description'   => $this->t('Check to optimize the stored options. Anything similar to defaults will not be stored, except those required by sub-modules and theme_slick(). Like you hand-code/ cherry-pick the needed options, and are smart enough to not repeat defaults, and free up memory. The rest are taken care of by JS. Uncheck only if theme_slick() can not satisfy the needs, and more hand-coded preprocess is needed which is less likely in most cases.'),
169       '#access'        => $slick->id() != 'default',
170       '#attributes'    => $tooltip,
171       '#wrapper_attributes' => ['class' => ['form-item--tooltip-wide']],
172     ];
173
174     if ($slick->id() == 'default') {
175       $form['breakpoints']['#suffix'] = '</div>';
176     }
177     else {
178       $form['optimized']['#suffix'] = '</div>';
179     }
180
181     if ($admin_css) {
182       $form['optimized']['#field_suffix'] = '&nbsp;';
183       $form['optimized']['#title_display'] = 'before';
184     }
185
186     return parent::form($form, $form_state);
187   }
188
189   /**
190    * {@inheritdoc}
191    */
192   public function submitForm(array &$form, FormStateInterface $form_state) {
193     parent::submitForm($form, $form_state);
194
195     // Optimized if so configured.
196     $slick   = $this->entity;
197     $default = $slick->id() == 'default';
198     if (!$default && !$form_state->isValueEmpty('optimized')) {
199       $defaults = $slick::defaultSettings();
200       $options  = $form_state->getValue('options');
201       $required = $this->getOptionsRequiredByTemplate();
202       $main     = array_diff_assoc($defaults, $required);
203       $settings = $form_state->getValue(['options', 'settings']);
204
205       // Cast the values.
206       $this->typecastOptionset($settings);
207
208       // Remove wasted dependent options if disabled, empty or not.
209       $slick->removeWastedDependentOptions($settings);
210
211       $main_settings = array_diff_assoc($settings, $main);
212       $slick->setSettings($main_settings);
213
214       $responsive_options = ['options', 'responsives', 'responsive'];
215       if ($responsives = $form_state->getValue($responsive_options)) {
216         foreach ($responsives as $delta => &$responsive) {
217           if (!empty($responsive['unslick'])) {
218             $slick->setResponsiveSettings([], $delta);
219           }
220           else {
221             $this->typecastOptionset($responsive['settings']);
222             $slick->removeWastedDependentOptions($responsive['settings']);
223
224             $responsive_settings = array_diff_assoc($responsive['settings'], $defaults);
225             $slick->setResponsiveSettings($responsive_settings, $delta);
226           }
227         }
228       }
229     }
230   }
231
232   /**
233    * Overrides Drupal\Core\Entity\EntityFormController::save().
234    *
235    * @todo revert #1497268, or use config_update instead.
236    */
237   public function save(array $form, FormStateInterface $form_state) {
238     $slick = $this->entity;
239
240     // Prevent leading and trailing spaces in slick names.
241     $slick->set('label', trim($slick->label()));
242     $slick->set('id', $slick->id());
243
244     $status        = $slick->save();
245     $label         = $slick->label();
246     $edit_link     = $slick->toLink($this->t('Edit'), 'edit-form')->toString();
247     $config_prefix = $slick->getEntityType()->getConfigPrefix();
248     $message       = ['@config_prefix' => $config_prefix, '%label' => $label];
249
250     $notice = [
251       '@config_prefix' => $config_prefix,
252       '%label' => $label,
253       'link' => $edit_link,
254     ];
255
256     if ($status == SAVED_UPDATED) {
257       // If we edited an existing entity.
258       // @todo #2278383.
259       drupal_set_message($this->t('@config_prefix %label has been updated.', $message));
260       $this->logger('slick')->notice('@config_prefix %label has been updated.', $notice);
261     }
262     else {
263       // If we created a new entity.
264       drupal_set_message($this->t('@config_prefix %label has been added.', $message));
265       $this->logger('slick')->notice('@config_prefix %label has been added.', $notice);
266     }
267   }
268
269   /**
270    * Handles switching the breakpoints based on the input value.
271    */
272   public function addBreakpoints($form, FormStateInterface $form_state) {
273     if (!$form_state->isValueEmpty('breakpoints')) {
274       $form_state->setValue('breakpoints_count', $form_state->getValue('breakpoints'));
275       if ($form_state->getValue('breakpoints') >= 6) {
276         $message = $this->t('You are trying to load too many Breakpoints. Try reducing it to reasonable numbers say, between 1 to 5.');
277         drupal_set_message($message, 'warning');
278       }
279     }
280
281     return $form['responsives']['responsive'];
282   }
283
284   /**
285    * Returns the typecast values.
286    *
287    * @param array $settings
288    *   An array of Optionset settings.
289    */
290   public function typecastOptionset(array &$settings = []) {
291     if (empty($settings)) {
292       return;
293     }
294
295     $defaults = Slick::defaultSettings();
296
297     foreach ($defaults as $name => $value) {
298       if (isset($settings[$name])) {
299         // Seems double is ignored, and causes a missing schema, unlike float.
300         $type = gettype($defaults[$name]);
301         $type = $type == 'double' ? 'float' : $type;
302
303         // Change float to integer if value is no longer float.
304         if ($name == 'edgeFriction') {
305           $type = $settings[$name] == '1' ? 'integer' : 'float';
306         }
307
308         settype($settings[$name], $type);
309       }
310     }
311   }
312
313   /**
314    * List of all easing methods available from jQuery Easing v1.3.
315    *
316    * @return array
317    *   An array of available jQuery Easing options as fallback for browsers that
318    *   don't support pure CSS easing.
319    */
320   public function getJsEasingOptions() {
321     if (!isset($this->jsEasingOptions)) {
322       $this->jsEasingOptions = [
323         'linear'           => 'Linear',
324         'swing'            => 'Swing',
325         'easeInQuad'       => 'easeInQuad',
326         'easeOutQuad'      => 'easeOutQuad',
327         'easeInOutQuad'    => 'easeInOutQuad',
328         'easeInCubic'      => 'easeInCubic',
329         'easeOutCubic'     => 'easeOutCubic',
330         'easeInOutCubic'   => 'easeInOutCubic',
331         'easeInQuart'      => 'easeInQuart',
332         'easeOutQuart'     => 'easeOutQuart',
333         'easeInOutQuart'   => 'easeInOutQuart',
334         'easeInQuint'      => 'easeInQuint',
335         'easeOutQuint'     => 'easeOutQuint',
336         'easeInOutQuint'   => 'easeInOutQuint',
337         'easeInSine'       => 'easeInSine',
338         'easeOutSine'      => 'easeOutSine',
339         'easeInOutSine'    => 'easeInOutSine',
340         'easeInExpo'       => 'easeInExpo',
341         'easeOutExpo'      => 'easeOutExpo',
342         'easeInOutExpo'    => 'easeInOutExpo',
343         'easeInCirc'       => 'easeInCirc',
344         'easeOutCirc'      => 'easeOutCirc',
345         'easeInOutCirc'    => 'easeInOutCirc',
346         'easeInElastic'    => 'easeInElastic',
347         'easeOutElastic'   => 'easeOutElastic',
348         'easeInOutElastic' => 'easeInOutElastic',
349         'easeInBack'       => 'easeInBack',
350         'easeOutBack'      => 'easeOutBack',
351         'easeInOutBack'    => 'easeInOutBack',
352         'easeInBounce'     => 'easeInBounce',
353         'easeOutBounce'    => 'easeOutBounce',
354         'easeInOutBounce'  => 'easeInOutBounce',
355       ];
356     }
357     return $this->jsEasingOptions;
358   }
359
360   /**
361    * List of available CSS easing methods.
362    *
363    * @param bool $map
364    *   Flag to output the array as is for further processing if TRUE.
365    *
366    * @return array
367    *   An array of CSS easings for select options, or all for the mappings.
368    *
369    * @see https://github.com/kenwheeler/slick/issues/118
370    * @see http://matthewlein.com/ceaser/
371    * @see http://www.w3.org/TR/css3-transitions/
372    */
373   public function getCssEasingOptions($map = FALSE) {
374     $css_easings = [];
375     $available_easings = [
376
377       // Defaults/ Native.
378       'ease'           => 'ease|ease',
379       'linear'         => 'linear|linear',
380       'ease-in'        => 'ease-in|ease-in',
381       'ease-out'       => 'ease-out|ease-out',
382       'swing'          => 'swing|ease-in-out',
383
384       // Penner Equations (approximated).
385       'easeInQuad'     => 'easeInQuad|cubic-bezier(0.550, 0.085, 0.680, 0.530)',
386       'easeInCubic'    => 'easeInCubic|cubic-bezier(0.550, 0.055, 0.675, 0.190)',
387       'easeInQuart'    => 'easeInQuart|cubic-bezier(0.895, 0.030, 0.685, 0.220)',
388       'easeInQuint'    => 'easeInQuint|cubic-bezier(0.755, 0.050, 0.855, 0.060)',
389       'easeInSine'     => 'easeInSine|cubic-bezier(0.470, 0.000, 0.745, 0.715)',
390       'easeInExpo'     => 'easeInExpo|cubic-bezier(0.950, 0.050, 0.795, 0.035)',
391       'easeInCirc'     => 'easeInCirc|cubic-bezier(0.600, 0.040, 0.980, 0.335)',
392       'easeInBack'     => 'easeInBack|cubic-bezier(0.600, -0.280, 0.735, 0.045)',
393       'easeOutQuad'    => 'easeOutQuad|cubic-bezier(0.250, 0.460, 0.450, 0.940)',
394       'easeOutCubic'   => 'easeOutCubic|cubic-bezier(0.215, 0.610, 0.355, 1.000)',
395       'easeOutQuart'   => 'easeOutQuart|cubic-bezier(0.165, 0.840, 0.440, 1.000)',
396       'easeOutQuint'   => 'easeOutQuint|cubic-bezier(0.230, 1.000, 0.320, 1.000)',
397       'easeOutSine'    => 'easeOutSine|cubic-bezier(0.390, 0.575, 0.565, 1.000)',
398       'easeOutExpo'    => 'easeOutExpo|cubic-bezier(0.190, 1.000, 0.220, 1.000)',
399       'easeOutCirc'    => 'easeOutCirc|cubic-bezier(0.075, 0.820, 0.165, 1.000)',
400       'easeOutBack'    => 'easeOutBack|cubic-bezier(0.175, 0.885, 0.320, 1.275)',
401       'easeInOutQuad'  => 'easeInOutQuad|cubic-bezier(0.455, 0.030, 0.515, 0.955)',
402       'easeInOutCubic' => 'easeInOutCubic|cubic-bezier(0.645, 0.045, 0.355, 1.000)',
403       'easeInOutQuart' => 'easeInOutQuart|cubic-bezier(0.770, 0.000, 0.175, 1.000)',
404       'easeInOutQuint' => 'easeInOutQuint|cubic-bezier(0.860, 0.000, 0.070, 1.000)',
405       'easeInOutSine'  => 'easeInOutSine|cubic-bezier(0.445, 0.050, 0.550, 0.950)',
406       'easeInOutExpo'  => 'easeInOutExpo|cubic-bezier(1.000, 0.000, 0.000, 1.000)',
407       'easeInOutCirc'  => 'easeInOutCirc|cubic-bezier(0.785, 0.135, 0.150, 0.860)',
408       'easeInOutBack'  => 'easeInOutBack|cubic-bezier(0.680, -0.550, 0.265, 1.550)',
409     ];
410
411     foreach ($available_easings as $key => $easing) {
412       list($readable_easing, $css_easing) = array_pad(array_map('trim', explode("|", $easing, 2)), 2, NULL);
413       $css_easings[$key] = $map ? $easing : $readable_easing;
414       unset($css_easing);
415     }
416     return $css_easings;
417   }
418
419   /**
420    * Defines options required by theme_slick(), used with optimized option.
421    */
422   public function getOptionsRequiredByTemplate() {
423     $options = [
424       'lazyLoad'     => 'ondemand',
425       'slidesToShow' => 1,
426     ];
427
428     $this->manager->getModuleHandler()->alter('slick_options_required_by_template', $options);
429     return $options;
430   }
431
432   /**
433    * Maps existing jQuery easing value to equivalent CSS easing methods.
434    *
435    * @param string $easing
436    *   The name of the human readable easing.
437    *
438    * @return string
439    *   A string of unfriendly bezier equivalent, or NULL.
440    */
441   public function getBezier($easing = NULL) {
442     $css_easing = '';
443     if ($easing) {
444       $easings = $this->getCssEasingOptions(TRUE);
445       list($readable_easing, $bezier) = array_pad(array_map('trim', explode("|", $easings[$easing], 2)), 2, NULL);
446       $css_easing = $bezier;
447       unset($readable_easing);
448     }
449     return $css_easing;
450   }
451
452 }