Dotclear

source: admin/js/common.js @ 3930:5007b8ff2be4

Revision 3930:5007b8ff2be4, 21.7 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Switching from inline JS variables to JSON script. Common js vars and msg

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

Sites map