~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/static/javascript/custom.js

  • Committer: William Grant
  • Date: 2012-02-03 01:29:46 UTC
  • mto: This revision was merged to the branch mainline in revision 467.
  • Revision ID: william.grant@canonical.com-20120203012946-17blutgatjtw8ixf
NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Y = YUI().use("base", "node", "io-base", "anim");
 
2
 
 
3
var global_timeout_id = null;
 
4
var global_search_request = null;
 
5
 
 
6
Y.on(
 
7
  'domready',
 
8
  function()
 
9
  {
 
10
    var search_box = Y.get('#q');
 
11
    if (!Y.Lang.isNull(search_box))
 
12
    {
 
13
      function get_suggestions() {
 
14
        var query = search_box.get('value');
 
15
        var url = global_path + 'search?query=' + query;
 
16
 
 
17
        if (!Y.Lang.isNull(global_search_request))
 
18
        {
 
19
          global_search_request.abort();
 
20
        }
 
21
        global_search_request = Y.io(
 
22
          url,
 
23
          {
 
24
            on: {complete: cool_search},
 
25
            arguments: [query]
 
26
          }
 
27
        );
 
28
 
 
29
        var region = search_box.get('region');
 
30
        var current_query = search_box.get('value');
 
31
 
 
32
        Y.get('#search_terms').setStyle('display', 'block');
 
33
        Y.get('#search_terms').setStyle('position', 'absolute');
 
34
        Y.get('#search_terms').setStyle('left', region.left);
 
35
        Y.get('#search_terms').setStyle('top', region.bottom);
 
36
        Y.get('#search_terms').set('innerHTML','Loading...');
 
37
      }
 
38
      search_box.on(
 
39
        "keyup",
 
40
        function(event)
 
41
        {
 
42
          if(search_box.get('value') == '')
 
43
          {
 
44
            Y.get('#search_terms').setStyle('display', 'none');
 
45
          }
 
46
          else
 
47
          {
 
48
            if (null != global_timeout_id)
 
49
            {
 
50
              clearTimeout(global_timeout_id);
 
51
            }
 
52
            global_timeout_id = setTimeout(get_suggestions, 200);
 
53
          }
 
54
        });
 
55
    }
 
56
  });
 
57
 
 
58
function cool_search(tid, response, query)
 
59
{
 
60
  var q = Y.get('#q');
 
61
  var region = q.get('region');
 
62
  var current_query = q.get('value');
 
63
  if (current_query == query)
 
64
  {
 
65
    Y.get('#search_terms').set('innerHTML', response.responseText);
 
66
    Y.get('#search_terms').setStyle('display', 'block');
 
67
    Y.get('#search_terms').setStyle('position', 'absolute');
 
68
    Y.get('#search_terms').setStyle('left', region.left);
 
69
    Y.get('#search_terms').setStyle('top', region.bottom);
 
70
  }
 
71
}
 
72
 
 
73
function hide_search()
 
74
{
 
75
  setTimeout("Y.get('#search_terms').setStyle('display','none')", 300);
 
76
}
 
77
 
 
78
function Collapsable(config)
 
79
{
 
80
  this.is_open = config.is_open;
 
81
  this.open_node = config.open_node;
 
82
  this.close_node = config.close_node;
 
83
  this.expand_icon = config.expand_icon;
 
84
  this.source = config.source;
 
85
  this.loading = config.loading;
 
86
  this.node_process = config.node_process;
 
87
  this.container = null;
 
88
  this.anim = null;
 
89
  this._loading = false;
 
90
}
 
91
 
 
92
function get_height(node) {
 
93
  node.setStyle('position', 'absolute');
 
94
  node.setStyle('top', -1000000000);
 
95
  node.setStyle('display', 'block');
 
96
  var height = node.get('region').height;
 
97
  node.setStyle('display', 'none');
 
98
  node.setStyle('position', 'static');
 
99
  node.setStyle('top', 'auto');
 
100
  return height;
 
101
}
 
102
 
 
103
Collapsable.prototype._animate = function (callback)
 
104
{
 
105
  if (this.anim) this.anim.stop();
 
106
 
 
107
  this.anim = new Y.Anim(
 
108
    {
 
109
      node: this.container,
 
110
      from: {
 
111
        marginBottom: this.container.getStyle('marginBottom')
 
112
      },
 
113
      to: {
 
114
        marginBottom: 0
 
115
      },
 
116
      duration: 0.2
 
117
    });
 
118
 
 
119
  this.anim.run();
 
120
  this.anim.on('end', this.animComplete, this, callback);
 
121
}
 
122
 
 
123
Collapsable.prototype._load_finished = function(tid, res, args)
 
124
{
 
125
  var l = res.responseText.split('\n');
 
126
  l.splice(0, 1);
 
127
  var newNode = Y.Node.create(l.join(''));
 
128
  if (this.node_process)
 
129
    this.node_process(newNode);
 
130
  this.source = null;
 
131
  newNode.setStyle('display', 'none');
 
132
  this.loading.ancestor().insertBefore(newNode, this.loading);
 
133
  var delta = this.loading.get('region').height - get_height(newNode);
 
134
  newNode.setStyle('display', 'block');
 
135
  this.container.setStyle('marginBottom', parseFloat(this.container.getStyle('marginBottom')) + delta);
 
136
  this.loading.ancestor().removeChild(this.loading);
 
137
  this._animate(args[0]);
 
138
};
 
139
 
 
140
Collapsable.prototype._ensure_container = function(callback)
 
141
{
 
142
  if (this.container == null) {
 
143
    this.container = Y.Node.create('<div></div>');
 
144
    if (this.closed_node) {
 
145
      this.closed_node.ancestor().replaceChild(
 
146
        this.container, this.closed_node);
 
147
      this.container.appendChild(this.closed_node);
 
148
      if (this.open_node) {
 
149
        this.container.appendChild(this.open_node);
 
150
      }
 
151
    }
 
152
    else {
 
153
      this.open_node.ancestor().replaceChild(
 
154
        this.container, this.open_node);
 
155
      this.container.appendChild(this.open_node);
 
156
    }
 
157
    var outer = Y.Node.create('<div style="overflow:hidden;"></div>');
 
158
    this.container.ancestor().replaceChild(outer, this.container);
 
159
    outer.appendChild(this.container);
 
160
  }
 
161
}
 
162
 
 
163
/* What happens when you click open.
 
164
 *
 
165
 * 1. The arrow flips to the expanded position.
 
166
 *
 
167
 * 2. If necessary, the div which will be running the animation is
 
168
 * created and the open/closed content stuffed into it (and has height
 
169
 * set to the height of the closed content).
 
170
 *
 
171
 * 3. The open content is shown and the closed content is closed.
 
172
 *
 
173
 * 4. The animation to expose all of the open content is started.
 
174
 *
 
175
 * 5. If we have to do ajax to load content, start the request.
 
176
 *
 
177
 * 6. When the request completes, parse the content into a node, run
 
178
 * the node_process callback over it and replace the spinner (assumed
 
179
 * to be appropriately contained in the open node) with the new node.
 
180
 *
 
181
 * 7. If the animation showing the open content has not completed,
 
182
 * stop it.
 
183
 *
 
184
 * 8. Start a new animation to show the rest of the new content.
 
185
 */
 
186
 
 
187
Collapsable.prototype.open = function(callback)
 
188
{
 
189
  this.expand_icon.set('src', expanded_icon_path);
 
190
 
 
191
  this._ensure_container();
 
192
 
 
193
  var open_height = get_height(this.open_node);
 
194
 
 
195
  var close_height;
 
196
  if (this.close_node) {
 
197
    close_height = this.close_node.get('region').height;
 
198
  }
 
199
  else {
 
200
    close_height = 0;
 
201
  }
 
202
 
 
203
  this.container.setStyle('marginBottom', close_height - open_height);
 
204
  if (this.close_node) {
 
205
    this.close_node.setStyle('display', 'none');
 
206
  }
 
207
  this.open_node.setStyle('display', 'block');
 
208
 
 
209
  this._animate(callback);
 
210
 
 
211
  if (this.source) {
 
212
    Y.io(
 
213
      this.source,
 
214
      {
 
215
        on: {complete: this._load_finished},
 
216
        arguments: [callback],
 
217
        context: this
 
218
      });
 
219
    return;
 
220
  }
 
221
 
 
222
};
 
223
 
 
224
Collapsable.prototype.animComplete = function(evt, callback)
 
225
{
 
226
  this.anim = null;
 
227
  if (this._loading) return;
 
228
  if (callback) callback();
 
229
  this.is_open = true;
 
230
};
 
231
 
 
232
Collapsable.prototype.close = function()
 
233
{
 
234
  this._ensure_container();
 
235
 
 
236
  var open_height = this.open_node.get('region').height;
 
237
 
 
238
  var close_height;
 
239
  if (this.close_node) {
 
240
    close_height = get_height(this.close_node);
 
241
  }
 
242
  else {
 
243
    close_height = 0;
 
244
  }
 
245
 
 
246
  var anim = new Y.Anim(
 
247
    {
 
248
      node: this.container,
 
249
      from: {
 
250
        marginBottom: 0
 
251
      },
 
252
      to: {
 
253
        marginBottom: close_height - open_height
 
254
      },
 
255
      duration: 0.2
 
256
    });
 
257
  anim.on("end", this.closeComplete, this);
 
258
  anim.run();
 
259
};
 
260
 
 
261
Collapsable.prototype.closeComplete = function () {
 
262
  this.open_node.setStyle('display', 'none');
 
263
  if (this.close_node) {
 
264
    this.close_node.setStyle('display', 'block');
 
265
  }
 
266
  this.container.setStyle('marginBottom', 0);
 
267
  this.expand_icon.set('src', collapsed_icon_path);
 
268
  this.is_open = false;
 
269
};
 
270
 
 
271
Collapsable.prototype.toggle = function()
 
272
{
 
273
  if (this.is_open)
 
274
  {
 
275
    this.close();
 
276
  }
 
277
  else
 
278
  {
 
279
    this.open();
 
280
  }
 
281
};
 
282
 
 
283
var notification_node = null;
 
284
/*
 
285
 * Display privacy notifications
 
286
 *
 
287
 * This should be called after the page has loaded e.g. on 'domready'.
 
288
 */
 
289
function setup_privacy_notification(config) {
 
290
    if (notification_node !== null) {
 
291
        return;
 
292
    }
 
293
    var notification_text = 'The information on this page is private';
 
294
    var hidden = true;
 
295
    var target_id = "loggerheadCont";
 
296
    if (config !== undefined) {
 
297
        if (config.notification_text !== undefined) {
 
298
            notification_text = config.notification_text;
 
299
        }
 
300
        if (config.hidden !== undefined) {
 
301
            hidden = config.hidden;
 
302
        }
 
303
        if (config.target_id !== undefined) {
 
304
            target_id = config.target_id;
 
305
        }
 
306
    }
 
307
    var id_selector = "#" + target_id;
 
308
    var main = Y.get(id_selector);
 
309
    notification_node = Y.Node.create('<div></div>')
 
310
        .addClass('global-notification');
 
311
    if (hidden) {
 
312
        notification_node.addClass('hidden');
 
313
    }
 
314
    var notification_span = Y.Node.create('<span></span>')
 
315
        .addClass('sprite')
 
316
        .addClass('notification-private');
 
317
    notification_node.set('innerHTML', notification_text);
 
318
    main.appendChild(notification_node);
 
319
    notification_node.appendChild(notification_span);
 
320
};
 
321
 
 
322
function display_privacy_notification() {
 
323
    /* Set a temporary class on the body for the feature flag,
 
324
     this is because we have no way to use feature flags in
 
325
     css directly. This should be removed if the feature
 
326
     is accepted. */
 
327
    var body = Y.get('body');
 
328
    body.addClass('feature-flag-bugs-private-notification-enabled');
 
329
    // Set the visible flag so that the content moves down.
 
330
    body.addClass('global-notification-visible');
 
331
 
 
332
    setup_privacy_notification();
 
333
    var global_notification = Y.get('.global-notification');
 
334
    if (global_notification.hasClass('hidden')) {
 
335
        global_notification.addClass('transparent');
 
336
        global_notification.removeClass('hidden');
 
337
 
 
338
        var fade_in = new Y.Anim({
 
339
            node: global_notification,
 
340
            to: {opacity: 1},
 
341
            duration: 0.3
 
342
        });
 
343
        var body_space = new Y.Anim({
 
344
            node: 'body',
 
345
            to: {'paddingTop': '40px'},
 
346
            duration: 0.2,
 
347
            easing: Y.Easing.easeOut
 
348
        });
 
349
        var black_link_space = new Y.Anim({
 
350
            node: '.black-link',
 
351
            to: {'top': '45px'},
 
352
            duration: 0.2,
 
353
            easing: Y.Easing.easeOut
 
354
        });
 
355
 
 
356
        fade_in.run();
 
357
        body_space.run();
 
358
        black_link_space.run();
 
359
    }
 
360
};
 
361
 
 
362
Y.on('domready', function() {
 
363
    var body = Y.get('body');
 
364
    if (body.hasClass('private')) {
 
365
        setup_privacy_notification();
 
366
        display_privacy_notification();
 
367
    }
 
368
});