Dotclear

source: admin/js/common.js @ 3882:254f3929b2c4

Revision 3882:254f3929b2c4, 21.5 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Actually the minifier did not like the template strings, so one step back (see rev [3881]) and fix the minifier

Line 
1/*global $, jQuery, dotclear_htmlFontSize */
2/*exported chainHandler */
3'use strict';
4
5/* Set some CSS variables here
6-------------------------------------------------------- */
7// set base font-size of body (62.5% default, usually : 50% to 75%)
8if (typeof dotclear_htmlFontSize !== 'undefined') {
9  document.documentElement.style.setProperty('--html-font-size', dotclear_htmlFontSize);
10}
11/* ChainHandler, py Peter van der Beken
12-------------------------------------------------------- */
13function chainHandler(obj, handlerName, handler) {
14  obj[handlerName] = (function(existingFunction) {
15    return function() {
16      handler.apply(this, arguments);
17      if (existingFunction) existingFunction.apply(this, arguments);
18    };
19  })(handlerName in obj ? obj[handlerName] : null);
20}
21/* jQuery extensions
22-------------------------------------------------------- */
23jQuery.fn.check = function() {
24  return this.each(function() {
25    if (this.checked != undefined) {
26      this.checked = true;
27    }
28  });
29};
30jQuery.fn.unCheck = function() {
31  return this.each(function() {
32    if (this.checked != undefined) {
33      this.checked = false;
34    }
35  });
36};
37jQuery.fn.setChecked = function(status) {
38  return this.each(function() {
39    if (this.checked != undefined) {
40      this.checked = status;
41    }
42  });
43};
44jQuery.fn.toggleCheck = function() {
45  return this.each(function() {
46    if (this.checked != undefined) {
47      this.checked = !this.checked;
48    }
49  });
50};
51jQuery.fn.enableShiftClick = function() {
52  this.click(function(event) {
53    if (event.shiftKey) {
54      if (dotclear.lastclicked != '') {
55        let range;
56        const trparent = $(this).parents('tr');
57        const id = `#${dotclear.lastclicked}`;
58        if (trparent.nextAll(id).length != 0) {
59          range = trparent.nextUntil(id);
60        } else {
61          range = trparent.prevUntil(id);
62        }
63        range.find('input[type=checkbox]').setChecked(dotclear.lastclickedstatus);
64        this.checked = dotclear.lastclickedstatus;
65      }
66    } else {
67      dotclear.lastclicked = $(this).parents('tr')[0].id;
68      dotclear.lastclickedstatus = this.checked;
69    }
70    return true;
71  });
72};
73jQuery.fn.toggleWithLegend = function(target, s) {
74  const defaults = {
75    img_on_txt: dotclear.img_plus_txt,
76    img_on_alt: dotclear.img_plus_alt,
77    img_off_txt: dotclear.img_minus_txt,
78    img_off_alt: dotclear.img_minus_alt,
79    unfolded_sections: dotclear.unfolded_sections,
80    hide: true,
81    speed: 0,
82    legend_click: false,
83    fn: false, // A function called on first display,
84    user_pref: false,
85    reverse_user_pref: false // Reverse cookie behavior
86  };
87  const p = jQuery.extend(defaults, s);
88  if (!target) {
89    return this;
90  }
91  const set_cookie = p.hide ^ p.reverse_cookie;
92  if (p.cookie && jQuery.cookie(p.cookie)) {
93    p.hide = p.reverse_cookie;
94  }
95  let set_user_pref = p.hide ^ p.reverse_user_pref;
96  if (p.user_pref && p.unfolded_sections !== undefined && (p.user_pref in p.unfolded_sections)) {
97    p.hide = p.reverse_user_pref;
98  }
99  const toggle = function(i, speed) {
100    speed = speed || 0;
101    const b = $(i).get(0);
102    if (p.hide) {
103      b.firstChild.data = p.img_on_txt;
104      b.setAttribute('value', p.img_on_txt);
105      b.setAttribute('aria-label', p.img_on_alt);
106      target.addClass('hide');
107    } else {
108      b.firstChild.data = p.img_off_txt;
109      b.setAttribute('value', p.img_off_txt);
110      b.setAttribute('aria-label', p.img_off_alt);
111      target.removeClass('hide');
112      if (p.fn) {
113        p.fn.apply(target);
114        p.fn = false;
115      }
116    }
117    if (p.cookie && set_cookie) {
118      if (p.hide ^ p.reverse_cookie) {
119        jQuery.cookie(p.cookie, '', {
120          expires: -1
121        });
122      } else {
123        jQuery.cookie(p.cookie, 1, {
124          expires: 30
125        });
126      }
127    }
128    p.hide = !p.hide;
129  };
130  return this.each(function() {
131    const b = document.createElement('button');
132    b.setAttribute('type', 'button');
133    b.className = 'details-cmd';
134    b.value = p.img_on_txt;
135    b.setAttribute('aria-label', p.img_on_alt);
136    const t = document.createTextNode(p.img_on_txt);
137    b.appendChild(t);
138
139    const ctarget = p.legend_click ? this : b;
140    $(ctarget).css('cursor', 'pointer');
141    if (p.legend_click) {
142      $(ctarget).find('label').css('cursor', 'pointer');
143    }
144    $(ctarget).click(function(e) {
145      if (p.user_pref && set_user_pref) {
146        jQuery.post('services.php', {
147          f: 'setSectionFold',
148          section: p.user_pref,
149          value: (p.hide ^ p.reverse_user_pref ? 1 : 0),
150          xd_check: dotclear.nonce
151        }, function() {});
152        jQuery.cookie(p.user_pref, '', {
153          expires: -1
154        });
155      }
156      toggle(b, p.speed);
157      e.preventDefault();
158      return false;
159    });
160    toggle($(b).get(0));
161    $(this).prepend(b);
162  });
163};
164
165(function($) {
166  $.expandContent = function(opts) {
167    if (opts == undefined || opts.callback == undefined || !$.isFunction(opts.callback)) {
168      return;
169    }
170    if (opts.line != undefined) {
171      multipleExpander(opts.line, opts.lines, opts.callback);
172    }
173    opts.lines.each(function() {
174      singleExpander(this, opts.callback);
175    });
176  };
177  const singleExpander = function(line, callback) {
178    $(`<button type="button" class="details-cmd" aria-label="${dotclear.img_plus_alt}">${dotclear.img_plus_txt}</button>`).click(function(e) {
179      toggleArrow(this);
180      callback(line, '', e);
181      e.preventDefault();
182    }).prependTo($(line).children().get(0)); // first td
183  };
184  const multipleExpander = function(line, lines, callback) {
185    $(`<button type="button" class="details-cmd" aria-label="${dotclear.img_plus_alt}">${dotclear.img_plus_txt}</button>`).click(function(e) {
186      var action = toggleArrow(this);
187      lines.each(function() {
188        toggleArrow(this.firstChild.firstChild, action);
189        callback(this, action, e);
190      });
191      e.preventDefault();
192    }).prependTo($(line).children().get(0)); // first td
193  };
194  const toggleArrow = function(button, action) {
195    action = action || '';
196    if (action == '') {
197      if (button.getAttribute('aria-label') == dotclear.img_plus_alt) {
198        action = 'open';
199      } else {
200        action = 'close';
201      }
202    }
203    if (action == 'open') {
204      button.firstChild.data = dotclear.img_minus_txt;
205      button.setAttribute('value', dotclear.img_minus_txt);
206      button.setAttribute('aria-label', dotclear.img_minus_alt);
207    } else {
208      button.firstChild.data = dotclear.img_plus_txt;
209      button.setAttribute('value', dotclear.img_plus_txt);
210      button.setAttribute('aria-label', dotclear.img_plus_alt);
211    }
212    return action;
213  };
214})(jQuery);
215jQuery.fn.helpViewer = function() {
216  if (this.length < 1) {
217    return this;
218  }
219  let select = $();
220  const p = {
221    img_on_txt: dotclear.img_plus_txt,
222    img_on_alt: dotclear.img_plus_alt,
223    img_off_txt: dotclear.img_minus_txt,
224    img_off_alt: dotclear.img_minus_alt
225  };
226  const This = this;
227  const toggle = function() {
228    $('#content').toggleClass('with-help');
229    if (document.all) {
230      if ($('#content').hasClass('with-help')) {
231        select = $('#content select:visible').hide();
232      } else {
233        select.show();
234      }
235    }
236    $('p#help-button span a').text($('#content').hasClass('with-help') ? dotclear.msg.help_hide : dotclear.msg.help);
237    sizeBox();
238    return false;
239  };
240  const sizeBox = function() {
241    This.css('height', 'auto');
242    if ($('#wrapper').height() > This.height()) {
243      This.css('height', $('#wrapper').height() + 'px');
244    }
245  };
246  const textToggler = function(o) {
247    const b = $(`<button type="button" class="details-cmd" aria-label="${p.img_on_alt}">${p.img_on_txt}</button>`);
248    o.css('cursor', 'pointer');
249    let hide = true;
250    o.prepend(' ').prepend(b);
251    o.click(function() {
252      $(this).nextAll().each(function() {
253        if ($(this).is('h4')) {
254          return false;
255        }
256        $(this).toggle();
257        sizeBox();
258        return true;
259      });
260      hide = !hide;
261      const img = $(this).find('button.details-cmd');
262      if (!hide) {
263        img.html(p.img_off_txt);
264        img.attr('value', p.img_off_txt);
265        img.attr('aria-label', p.img_off_alt);
266      } else {
267        img.html(p.img_on_txt);
268        img.attr('value', p.img_on_txt);
269        img.attr('aria-label', p.img_on_alt);
270      }
271    });
272  };
273  this.addClass('help-box');
274  this.find('>hr').remove();
275  this.find('h4').each(function() {
276    textToggler($(this));
277  });
278  this.find('h4:first').nextAll('*:not(h4)').hide();
279  sizeBox();
280  const img = $(`<p id="help-button"><span><a href="">${dotclear.msg.help}</a></span></p>`);
281  img.click(function(e) {
282    e.preventDefault();
283    return toggle();
284  });
285  $('#content').append(img);
286  // listen for scroll
287  const peInPage = $('#help-button').offset().top;
288  $('#help-button').addClass('floatable');
289  const peInFloat = $('#help-button').offset().top - $(window).scrollTop();
290  $('#help-button').removeClass('floatable');
291  $(window).scroll(function() {
292    if ($(window).scrollTop() >= peInPage - peInFloat) {
293      $('#help-button').addClass('floatable');
294    } else {
295      $('#help-button').removeClass('floatable');
296    }
297  });
298  return this;
299};
300
301/* Dotclear common object
302-------------------------------------------------------- */
303const dotclear = {
304  msg: {},
305
306  enterKeyInForm: function(frm_id, ok_id, cancel_id) {
307    $(frm_id + ':not(' + cancel_id + ')').keyup(function(e) {
308      if ((e.key == 'Enter') && ($(ok_id).prop('disabled') !== true)) {
309        e.preventDefault();
310        e.stopPropagation();
311        $(ok_id).trigger('click');
312      }
313    });
314  },
315
316  condSubmit: function(chkboxes, target) {
317    const checkboxes = $(chkboxes),
318      submitButt = $(target);
319    if (checkboxes === undefined || submitButt === undefined) {
320      return;
321    }
322    // Set initial state
323    submitButt.attr('disabled', !checkboxes.is(':checked'));
324    if (!checkboxes.is(':checked')) {
325      submitButt.addClass('disabled');
326    } else {
327      submitButt.removeClass('disabled');
328    }
329    checkboxes.click(function() {
330      // Update target state
331      submitButt.attr('disabled', !checkboxes.is(':checked'));
332      if (!checkboxes.is(':checked')) {
333        submitButt.addClass('disabled');
334      } else {
335        submitButt.removeClass('disabled');
336      }
337    });
338  },
339
340  hideLockable: function() {
341    $('div.lockable').each(function() {
342      const current_lockable_div = this;
343      $(this).find('p.form-note').hide();
344      $(this).find('input').each(function() {
345        this.disabled = true;
346        $(this).width(`${$(this).width() - 14}px`);
347        const imgE = document.createElement('img');
348        imgE.src = 'images/locker.png';
349        imgE.style.position = 'absolute';
350        imgE.style.top = '1.8em';
351        imgE.style.left = `${$(this).width() + 14}px`;
352        imgE.alt = dotclear.msg.click_to_unlock;
353        $(imgE).css('cursor', 'pointer');
354        $(imgE).click(function() {
355          $(this).hide();
356          $(this).prev('input').each(function() {
357            this.disabled = false;
358            $(this).width(($(this).width() + 14) + 'px');
359          });
360          $(current_lockable_div).find('p.form-note').show();
361        });
362        $(this).parent().css('position', 'relative');
363        $(this).after(imgE);
364      });
365    });
366  },
367
368  checkboxesHelpers: function(e, target, c, s) {
369    $(e).append(document.createTextNode(dotclear.msg.to_select));
370    $(e).append(document.createTextNode(' '));
371    $(`<button type="button" class="checkbox-helper select-all">${dotclear.msg.select_all}</button>`).click(function() {
372      if (target !== undefined) {
373        target.check();
374      } else {
375        $(e).parents('form').find('input[type="checkbox"]').check();
376      }
377      if (c !== undefined && s !== undefined) {
378        dotclear.condSubmit(c, s);
379      }
380      return false;
381    }).appendTo($(e));
382    $(e).append(document.createTextNode(' '));
383    $(`<button type="button" class="checkbox-helper select-none">${dotclear.msg.no_selection}</button>`).click(function() {
384      if (target !== undefined) {
385        target.unCheck();
386      } else {
387        $(e).parents('form').find('input[type="checkbox"]').unCheck();
388      }
389      if (c !== undefined && s !== undefined) {
390        dotclear.condSubmit(c, s);
391      }
392      return false;
393    }).appendTo($(e));
394    $(e).append(document.createTextNode(' '));
395    $(`<button type="button" class="checkbox-helper select-reverse">${dotclear.msg.invert_sel}</button>`).click(function() {
396      if (target !== undefined) {
397        target.toggleCheck();
398      } else {
399        $(e).parents('form').find('input[type="checkbox"]').toggleCheck();
400      }
401      if (c !== undefined && s !== undefined) {
402        dotclear.condSubmit(c, s);
403      }
404      return false;
405    }).appendTo($(e));
406  },
407
408  postsActionsHelper: function() {
409    $('#form-entries').submit(function() {
410      const action = $(this).find('select[name="action"]').val();
411      if (action === undefined) {
412        return;
413      }
414      let checked = false;
415      $(this).find('input[name="entries[]"]').each(function() {
416        if (this.checked) {
417          checked = true;
418        }
419      });
420      if (!checked) {
421        return false;
422      }
423      if (action == 'delete') {
424        return window.confirm(dotclear.msg.confirm_delete_posts.replace('%s', $('input[name="entries[]"]:checked').size()));
425      }
426      return true;
427    });
428  },
429
430  commentsActionsHelper: function() {
431    $('#form-comments').submit(function() {
432      const action = $(this).find('select[name="action"]').val();
433      let checked = false;
434      $(this).find('input[name="comments[]"]').each(function() {
435        if (this.checked) {
436          checked = true;
437        }
438      });
439      if (!checked) {
440        return false;
441      }
442      if (action == 'delete') {
443        return window.confirm(dotclear.msg.confirm_delete_comments.replace('%s', $('input[name="comments[]"]:checked').size()));
444      }
445      return true;
446    });
447  },
448
449  outgoingLinks: function(target) {
450    $(target).filter(function() {
451      return ((this.hostname && this.hostname != location.hostname && !$(this).hasClass('modal') && !$(this).hasClass('modal-image')) || $(this).hasClass('outgoing'));
452    }).each(function() {
453      $(this).prop('title', `${$(this).prop('title')} (${dotclear.msg.new_window})`);
454      if (!$(this).hasClass('outgoing')) {
455        $(this).append('&nbsp;<img class="outgoing-js" src="images/outgoing-link.svg" alt=""/>');
456      }
457    }).click(function(e) {
458      e.preventDefault();
459      window.open($(this).attr('href'));
460    });
461  },
462
463  badge: function($elt, options = null) {
464    // Cope with selector given as string or DOM element rather than a jQuery object
465    if (typeof $elt === 'string' || $elt instanceof Element) {
466      $elt = $($elt);
467    }
468
469    // Return if elt does not exist
470    if (!$elt.length) return;
471
472    // Cope with options
473    const opt = $.extend({
474      /* sibling: define if the given element is a sibling of the badge or it's parent
475       *  true: use $elt.after() to add badge
476       *  false: use $elt.parent().append() to add badge (default)
477       */
478      sibling: false,
479      /* id: badge unique class
480       *  this class will be used to delete all corresponding badge (used for removing and updating)
481       */
482      id: 'default',
483      /* remove: will remove the badge if set to true */
484      remove: false,
485      /* value: badge value */
486      value: null,
487      /* inline: if set to true, the badge is an inline element (useful for menu item) rather than a block */
488      inline: false,
489      /* icon: if set to true, the badge is attached to a dashboard icon (needed for correct positionning) */
490      icon: false,
491      /* type: Override default background (which may vary)
492       *  by default badge background are soft grey for dashboard icons (see opt.icon) and bright red for all other elements
493       *  possible values:
494       *    'std':  bright red
495       *    'info': blue
496       *    'soft': soft grey
497       */
498      type: '',
499      /* left: display badge on the left rather than on the right (unused for inline badge) */
500      left: false,
501      /* noborder: do not display the badge border */
502      noborder: false,
503      /* small: use a smaller font-size */
504      small: false,
505      /* classes: additionnal badge classes */
506      classes: ''
507    }, options);
508
509    // Set some constants
510    const classid = `span.badge.badge-${opt.id}`; // Pseudo unique class
511
512    // Set badgeable class to elt parent's (if sibling) or elt itself, if it is necessary
513    const $parent = (opt.sibling ? $elt.parent() : $elt);
514    if (!opt.inline && !opt.remove && !$parent.hasClass('badgeable')) {
515      $parent.addClass('badgeable');
516    }
517
518    // Remove existing badge if exists
519    const $badge = (opt.sibling ? $parent.children(classid) : $elt.children(classid));
520    if ($badge.length) {
521      $badge.remove();
522    }
523
524    // Add the new badge if any
525    if (!opt.remove && opt.value !== null) {
526      // Compose badge classes
527      const cls = `badge badge-${opt.id} \
528${opt.inline ? 'badge-inline' : 'badge-block'}\
529${opt.icon ? ' badge-icon' : ''}\
530${opt.type !== '' ? ` badge-${opt.type}` : ''}\
531${opt.left ? ' badge-left' : ''}\
532${opt.noborder ? ' badge-noborder' : ''}\
533${opt.small ? ' badge-small' : ''}\
534${opt.classes !== '' ? ` ${opt.classes}` : ''}`;
535      // Compose badge
536      const xml = `<span class="${cls}" aria-hidden="true">${opt.value}</span>`;
537      if (opt.sibling) {
538        // Add badge after it's sibling
539        $elt.after(xml);
540      } else {
541        // Append badge to the elt
542        $elt.append(xml);
543      }
544    }
545  }
546};
547
548/* On document ready
549-------------------------------------------------------- */
550$(function() {
551  // remove class no-js from html tag; cf style/default.css for examples
552  $('body').removeClass('no-js').addClass('with-js');
553  $('body').contents().each(function() {
554    if (this.nodeType == 8) {
555      let data = this.data;
556      data = data.replace(/ /g, '&nbsp;').replace(/\n/g, '<br/>');
557      $(`<span class="tooltip" aria-hidden="true">${$('#footer a').prop('title')}${data}</span>`).appendTo('#footer a');
558    }
559  });
560  // manage outgoing links
561  dotclear.outgoingLinks('a');
562  // Popups: dealing with Escape key fired
563  $('#dotclear-admin.popup').keyup(function(e) {
564    if (e.key == 'Escape') {
565      e.preventDefault();
566      window.close();
567      return false;
568    }
569  });
570  // Blog switcher
571  $('#switchblog').change(function() {
572    this.form.submit();
573  });
574  const menu_settings = {
575    img_on_src: dotclear.img_menu_off,
576    img_off_src: dotclear.img_menu_on,
577    legend_click: true,
578    speed: 100
579  };
580  $('#blog-menu h3:first').toggleWithLegend($('#blog-menu ul:first'), $.extend({
581    user_pref: 'dc_blog_menu'
582  }, menu_settings));
583  $('#system-menu h3:first').toggleWithLegend($('#system-menu ul:first'), $.extend({
584    user_pref: 'dc_system_menu'
585  }, menu_settings));
586  $('#plugins-menu h3:first').toggleWithLegend($('#plugins-menu ul:first'), $.extend({
587    user_pref: 'dc_plugins_menu'
588  }, menu_settings));
589  $('#favorites-menu h3:first').toggleWithLegend($('#favorites-menu ul:first'), $.extend({
590    user_pref: 'dc_favorites_menu',
591    hide: false,
592    reverse_user_pref: true
593  }, menu_settings));
594  $('#help').helpViewer();
595  // Notices
596  $('p.success,p.warning,p.error,div.error').each(function() {
597    $(this).addClass('close-notice-parent');
598    $(this).append(`<button class="close-notice" type="button"><img src="images/close.png" alt="${dotclear.msg.close_notice}" /></button>`);
599  });
600  $('button.close-notice').click(function(e) {
601    e.preventDefault();
602    $(this).parent().hide();
603  });
604  // Password
605  $('form:has(input[type=password][name=your_pwd])').submit(function() {
606    const e = this.elements.your_pwd;
607    if (e.value == '') {
608      $(e).addClass('missing').focusout(function() {
609        $(this).removeClass('missing');
610      });
611      e.focus();
612      return false;
613    }
614    return true;
615  });
616  // Cope with ellipsis'ed cells
617  $('table .maximal').each(function() {
618    if (this.offsetWidth < this.scrollWidth) {
619      if (this.title == '') {
620        this.title = this.innerText;
621        $(this).addClass('ellipsis');
622      }
623    }
624  });
625  $('table .maximal.ellipsis a').each(function() {
626    if (this.title == '') {
627      this.title = this.innerText;
628    }
629  });
630  // Advanced users
631  if (dotclear.hideMoreInfo) {
632    $('.more-info,.form-note:not(.warn,.warning,.info)').addClass('no-more-info');
633  }
634  // Ajax loader activity indicator
635  if (dotclear.showAjaxLoader) {
636    $(document).ajaxStart(function() {
637      $('body').addClass('ajax-loader');
638      $('div.ajax-loader').show();
639    });
640    $(document).ajaxStop(function() {
641      $('body').removeClass('ajax-loader');
642      $('div.ajax-loader').hide();
643    });
644  }
645  // Main menu collapser
646  const objMain = $('#wrapper');
647
648  function showSidebar() {
649    // Show sidebar
650    objMain.removeClass('hide-mm');
651    $.cookie('sidebar-pref', null, {
652      expires: 30
653    });
654  }
655
656  function hideSidebar() {
657    // Hide sidebar
658    objMain.addClass('hide-mm');
659    $.cookie('sidebar-pref', 'hide-mm', {
660      expires: 30
661    });
662  }
663  // Sidebar separator
664  $('#collapser').click(function(e) {
665    e.preventDefault();
666    if (objMain.hasClass('hide-mm')) {
667      showSidebar();
668      $('#main-menu input#qx').focus();
669    } else {
670      hideSidebar();
671      $('#content a.go_home').focus();
672    }
673  });
674  if ($.cookie('sidebar-pref') == 'hide-mm') {
675    objMain.addClass('hide-mm');
676  } else {
677    objMain.removeClass('hide-mm');
678  }
679  // totop scroll
680  $(window).scroll(function() {
681    if ($(this).scrollTop() != 0) {
682      $('#gototop').fadeIn();
683    } else {
684      $('#gototop').fadeOut();
685    }
686  });
687  $('#gototop').click(function(e) {
688    $('body,html').animate({
689      scrollTop: 0
690    }, 800);
691    e.preventDefault();
692  });
693});
Note: See TracBrowser for help on using the repository browser.

Sites map