Projet

Général

Profil

Paste
Télécharger (8,28 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / flag / theme / flag.js @ 87dbc3bf

1
(function ($) {
2

    
3
/**
4
 * Terminology:
5
 *
6
 *   "Link" means "Everything which is in flag.tpl.php" --and this may contain
7
 *   much more than the <A> element. On the other hand, when we speak
8
 *   specifically of the <A> element, we say "element" or "the <A> element".
9
 */
10

    
11
/**
12
 * The main behavior to perform AJAX toggling of links.
13
 */
14
Drupal.flagLink = function(context) {
15
  /**
16
   * Helper function. Updates a link's HTML with a new one.
17
   *
18
   * @param element
19
   *   The <A> element.
20
   * @return
21
   *   The new link.
22
   */
23
  function updateLink(element, newHtml) {
24
    var $newLink = $(newHtml);
25

    
26
    // Initially hide the message so we can fade it in.
27
    $('.flag-message', $newLink).css('display', 'none');
28

    
29
    // Reattach the behavior to the new <A> element. This element
30
    // is either whithin the wrapper or it is the outer element itself.
31
    var $nucleus = $newLink.is('a') ? $newLink : $('a.flag', $newLink);
32
    $nucleus.addClass('flag-processed').click(flagClick);
33

    
34
    // Find the wrapper of the old link.
35
    var $wrapper = $(element).parents('.flag-wrapper:first');
36
    // Replace the old link with the new one.
37
    $wrapper.after($newLink).remove();
38
    Drupal.attachBehaviors($newLink.get(0));
39

    
40
    $('.flag-message', $newLink).fadeIn();
41
    setTimeout(function(){ $('.flag-message.flag-auto-remove', $newLink).fadeOut() }, 3000);
42
    return $newLink.get(0);
43
  }
44

    
45
  /**
46
   * A click handler that is attached to all <A class="flag"> elements.
47
   */
48
  function flagClick(event) {
49
    // Prevent the default browser click handler
50
    event.preventDefault();
51

    
52
    // 'this' won't point to the element when it's inside the ajax closures,
53
    // so we reference it using a variable.
54
    var element = this;
55

    
56
    // While waiting for a server response, the wrapper will have a
57
    // 'flag-waiting' class. Themers are thus able to style the link
58
    // differently, e.g., by displaying a throbber.
59
    var $wrapper = $(element).parents('.flag-wrapper');
60
    if ($wrapper.is('.flag-waiting')) {
61
      // Guard against double-clicks.
62
      return false;
63
    }
64
    $wrapper.addClass('flag-waiting');
65

    
66
    // Hide any other active messages.
67
    $('span.flag-message:visible').fadeOut();
68

    
69
    // Send POST request
70
    $.ajax({
71
      type: 'POST',
72
      url: element.href,
73
      data: { js: true },
74
      dataType: 'json',
75
      success: function (data) {
76
        data.link = $wrapper.get(0);
77
        $.event.trigger('flagGlobalBeforeLinkUpdate', [data]);
78
        if (!data.preventDefault) { // A handler may cancel updating the link.
79
          data.link = updateLink(element, data.newLink);
80
        }
81

    
82
        // Find all the link wrappers on the page for this flag, but exclude
83
        // the triggering element because Flag's own javascript updates it.
84
        var $wrappers = $('.flag-wrapper.flag-' + data.flagName.flagNameToCSS() + '-' + data.contentId).not(data.link);
85
        var $newLink = $(data.newLink);
86

    
87
        // Hide message, because we want the message to be shown on the triggering element alone.
88
        $('.flag-message', $newLink).hide();
89

    
90
        // Finally, update the page.
91
        $wrappers = $newLink.replaceAll($wrappers);
92
        Drupal.attachBehaviors($wrappers.parent());
93

    
94
        $.event.trigger('flagGlobalAfterLinkUpdate', [data]);
95
      },
96
      error: function (xmlhttp) {
97
        alert('An HTTP error '+ xmlhttp.status +' occurred.\n'+ element.href);
98
        $wrapper.removeClass('flag-waiting');
99
      }
100
    });
101
  }
102

    
103
  $('a.flag-link-toggle:not(.flag-processed)', context).addClass('flag-processed').click(flagClick);
104
};
105

    
106
/**
107
 * Prevent anonymous flagging unless the user has JavaScript enabled.
108
 */
109
Drupal.flagAnonymousLinks = function(context) {
110
  $('a.flag:not(.flag-anonymous-processed)', context).each(function() {
111
    this.href += (this.href.match(/\?/) ? '&' : '?') + 'has_js=1';
112
    $(this).addClass('flag-anonymous-processed');
113
  });
114
}
115

    
116
String.prototype.flagNameToCSS = function() {
117
  return this.replace(/_/g, '-');
118
}
119

    
120
/**
121
 * A behavior specifically for anonymous users. Update links to the proper state.
122
 */
123
Drupal.flagAnonymousLinkTemplates = function(context) {
124
  // Swap in current links. Cookies are set by PHP's setcookie() upon flagging.
125

    
126
  var templates = Drupal.settings.flag.templates;
127

    
128
  // Build a list of user-flags.
129
  var userFlags = Drupal.flagCookie('flags');
130
  if (userFlags) {
131
    userFlags = userFlags.split('+');
132
    for (var n in userFlags) {
133
      var flagInfo = userFlags[n].match(/(\w+)_(\d+)/);
134
      var flagName = flagInfo[1];
135
      var contentId = flagInfo[2];
136
      // User flags always default to off and the JavaScript toggles them on.
137
      if (templates[flagName + '_' + contentId]) {
138
        $('.flag-' + flagName.flagNameToCSS() + '-' + contentId, context).after(templates[flagName + '_' + contentId]).remove();
139
      }
140
    }
141
  }
142

    
143
  // Build a list of global flags.
144
  var globalFlags = document.cookie.match(/flag_global_(\w+)_(\d+)=([01])/g);
145
  if (globalFlags) {
146
    for (var n in globalFlags) {
147
      var flagInfo = globalFlags[n].match(/flag_global_(\w+)_(\d+)=([01])/);
148
      var flagName = flagInfo[1];
149
      var contentId = flagInfo[2];
150
      var flagState = (flagInfo[3] == '1') ? 'flag' : 'unflag';
151
      // Global flags are tricky, they may or may not be flagged in the page
152
      // cache. The template always contains the opposite of the current state.
153
      // So when checking global flag cookies, we need to make sure that we
154
      // don't swap out the link when it's already in the correct state.
155
      if (templates[flagName + '_' + contentId]) {
156
        $('.flag-' + flagName.flagNameToCSS() + '-' + contentId, context).each(function() {
157
          if ($(this).find('.' + flagState + '-action').size()) {
158
            $(this).after(templates[flagName + '_' + contentId]).remove();
159
          }
160
        });
161
      }
162
    }
163
  }
164
}
165

    
166
/**
167
 * Utility function used to set Flag cookies.
168
 *
169
 * Note this is a direct copy of the jQuery cookie library.
170
 * Written by Klaus Hartl.
171
 */
172
Drupal.flagCookie = function(name, value, options) {
173
  if (typeof value != 'undefined') { // name and value given, set cookie
174
    options = options || {};
175
    if (value === null) {
176
      value = '';
177
      options = $.extend({}, options); // clone object since it's unexpected behavior if the expired property were changed
178
      options.expires = -1;
179
    }
180
    var expires = '';
181
    if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
182
      var date;
183
      if (typeof options.expires == 'number') {
184
        date = new Date();
185
        date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
186
      } else {
187
        date = options.expires;
188
      }
189
      expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
190
    }
191
    // NOTE Needed to parenthesize options.path and options.domain
192
    // in the following expressions, otherwise they evaluate to undefined
193
    // in the packed version for some reason...
194
    var path = options.path ? '; path=' + (options.path) : '';
195
    var domain = options.domain ? '; domain=' + (options.domain) : '';
196
    var secure = options.secure ? '; secure' : '';
197
    document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
198
  } else { // only name given, get cookie
199
    var cookieValue = null;
200
    if (document.cookie && document.cookie != '') {
201
      var cookies = document.cookie.split(';');
202
      for (var i = 0; i < cookies.length; i++) {
203
        var cookie = jQuery.trim(cookies[i]);
204
        // Does this cookie string begin with the name we want?
205
        if (cookie.substring(0, name.length + 1) == (name + '=')) {
206
          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
207
          break;
208
        }
209
      }
210
    }
211
    return cookieValue;
212
  }
213
};
214

    
215
Drupal.behaviors.flagLink = {};
216
Drupal.behaviors.flagLink.attach = function(context) {
217
  // For anonymous users with the page cache enabled, swap out links with their
218
  // current state for the user.
219
  if (Drupal.settings.flag && Drupal.settings.flag.templates) {
220
    Drupal.flagAnonymousLinkTemplates(context);
221
  }
222

    
223
  // For all anonymous users, require JavaScript for flagging to prevent spiders
224
  // from flagging things inadvertently.
225
  if (Drupal.settings.flag && Drupal.settings.flag.anonymous) {
226
    Drupal.flagAnonymousLinks(context);
227
  }
228

    
229
  // On load, bind the click behavior for all links on the page.
230
  Drupal.flagLink(context);
231
};
232

    
233
})(jQuery);